Troubleshooting Guide
This guide covers the most common problems users and developers encounter with Vibrante-Node v2.2.1, organized by symptom. For each issue you will find: what causes it, how to diagnose it, and how to fix it.
1. Installation Issues
PyQt5 not found
Symptom: ModuleNotFoundError: No module named 'PyQt5' at startup.
Cause: PyQt5 is not installed in the active Python environment.
Fix:
pip install PyQt5
If you are on Python 3.12 or newer and PyQt5 installation fails with a wheel error, use Python 3.11:
python3.11 -m venv .venv
.venv\Scripts\activate # Windows
pip install -r requirements.txt
On Linux, also install the system Qt libraries:
sudo apt-get install python3-pyqt5
# or
pip install PyQt5 --config-settings --confirm-license=
QScintilla missing — code editor shows plain text
Symptom: The Python editor in the Node Builder or Script Editor shows as a plain text box without syntax highlighting or autocompletion.
Cause: QScintilla (PyQt5.Qsci) is not installed. This is not an error — the app falls back to a QPlainTextEdit-based editor automatically.
Fix (optional):
pip install QScintilla
Restart the app after installing. If QScintilla is installed but the enhanced editor still doesn't appear, check that the version matches your PyQt5 version:
pip show QScintilla PyQt5
Important: This fallback is intentional. Never re-raise the ImportError in code_editor.py. If you see ImportError: QScintilla is required as a hard crash, someone has re-added the raise — revert that change.
Pydantic version errors
Symptom: ValidationError, AttributeError: model_fields, or TypeError: __init__() got an unexpected keyword argument when loading workflows.
Cause: Vibrante-Node requires pydantic v2. Pydantic v1 has an incompatible API.
Diagnosis:
python -c "import pydantic; print(pydantic.VERSION)"
Fix:
pip install "pydantic>=2.0"
If another package in your environment pins pydantic v1, use a separate virtual environment for Vibrante-Node.
No module named 'src'
Symptom: ModuleNotFoundError: No module named 'src' when running scripts or tests.
Cause: Python cannot find the src package because the working directory is not the project root, or the project root is not in sys.path.
Fix for running the app:
# Always run from the project root (node_based_app/)
cd /path/to/node_based_app
python -m src.main
Fix for scripts:
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src.core.engine import NetworkExecutor
Fix for tests: The pytest.ini at the project root sets pythonpath = . which handles this automatically. Always run pytest from the project root.
2. App Won't Start
Qt platform plugin error on Windows
Symptom:
qt.qpa.plugin: Could not find the Qt platform plugin "windows" in ""
This application failed to start because no Qt platform plugin could be initialized.
Cause: Qt's DLLs are not in the path, or the virtual environment is missing Qt platform files.
Fix:
pip install --force-reinstall PyQt5 PyQt5-Qt5 PyQt5-sip
If using a PyInstaller build (the .exe), ensure the platforms/ folder exists next to the executable:
vibrante_node.exe
platforms/
qwindows.dll
Linux: no display / cannot connect to X server
Symptom: qt.qpa.xcb: could not connect to display or DISPLAY is not set.
Fix for headless servers: Use a virtual display:
pip install pyqtgraph # optional
Xvfb :99 -screen 0 1920x1080x24 &
export DISPLAY=:99
python -m src.main
For running in CI or Docker, use offscreen platform:
QT_QPA_PLATFORM=offscreen python -m src.main
Fix for desktop Linux: Ensure $DISPLAY is set and the current user has access to the X session:
export DISPLAY=:0
xhost +local:
Crash on startup with no error message
Symptom: App closes immediately with no dialog or log output.
Check: Look for crash.log in the project root:
cat crash.log
The crash log captures Python tracebacks that occur before the UI is ready. Common causes:
- Corrupt settings.json in ~/.vibrante_node/
- A broken node JSON file in nodes/ that crashes the registry loader
- A missing icon file referenced in a node JSON
Fix for corrupt settings:
# Delete user settings to reset to defaults
rm ~/.vibrante_node/settings.json # Linux/macOS
del %USERPROFILE%\.vibrante_node\settings.json # Windows
Fix for broken node JSON: Remove or fix the offending file in nodes/. The crash log will name the file.
3. Nodes Not Appearing in Library
Node JSON file not loaded
Symptom: A .json file in nodes/ is not appearing in the Node Library panel.
Diagnosis checklist:
1. Is the file valid JSON? Validate with: python -m json.tool nodes/my_node.json
2. Does the file have "node_id" and "name" fields?
3. Is "python_code" present and non-empty?
4. Does the python_code contain a register_node() function that returns the class?
5. Is there a syntax error in the python_code string?
Checking for syntax errors in python_code:
import json, ast
with open("nodes/my_node.json") as f:
data = json.load(f)
try:
ast.parse(data["python_code"])
print("Syntax OK")
except SyntaxError as e:
print(f"Syntax error: {e}")
Houdini nodes missing from library
Symptom: Houdini-specific nodes from plugins/houdini/v_nodes_houdini/ do not appear after launching from Houdini.
Cause: v_nodes_dir environment variable is not set, or NodeRegistry.load_all() was called instead of load_all_with_extras().
Diagnosis: In the app, open the Python console (if available) and run:
import os
print(os.environ.get("v_nodes_dir", "NOT SET"))
Fix:
- Ensure you launched the app from Houdini via Vibrante-Node → Launch (not by running python -m src.main directly).
- In src/ui/window.py, confirm load_all_with_extras(bundled_nodes) is called, not load_all(bundled_nodes).
register_node() missing
Symptom: Node file loads without error but class is not accessible.
Cause: The python_code string is missing the register_node() function at the module level.
Required:
def register_node():
return MyNodeClass
This function must be at module level (not inside the class) and must return the class itself, not an instance.
4. Workflow Won't Run
No entry node / nothing executes
Symptom: Click Run — nothing happens. Log panel is empty.
Cause: The engine cannot find a starting node. The execution engine starts from nodes that have no exec_in connection (their exec_in port is unconnected).
Fix: Ensure at least one node in your workflow has no incoming exec_in connection. That node is the entry point.
If all nodes have exec_in wired (a closed loop), the engine has no starting point. Break the loop by disconnecting one exec_in.
Circular dependency error
Symptom: CyclicDependencyError or RecursionError in the log.
Cause: A data connection creates a cycle (Node A output → Node B input → Node A input).
Fix: Inspect the data connections (non-exec wires). Cycles in exec flow are handled differently than data cycles. A cycle in data connections is always an error — restructure your workflow to use SetVariable / GetVariable to break the cycle.
All nodes are bypassed
Symptom: Run completes instantly, no output, log says nodes were skipped.
Fix: Right-click each node and check if Bypass is checked. Bypassed nodes appear dimmed. Un-bypass them via right-click → Bypass (toggle off).
5. Node Produces Wrong Output
None values flowing through wires
Symptom: A downstream node receives None where it expects a string or list.
Diagnosis:
1. Hover the wire from the upstream node — the tooltip shows the last value.
2. Check the upstream node's execute() return dict. Is the output key present?
3. Check for an exception in the upstream node: look for red entries in the log panel.
Common cause: The upstream node raised an exception, its except block returned {"exec_out": True} without the expected output key, so downstream receives None from .get().
Fix: Ensure every code path in execute() returns all expected output keys:
return {
"result": result_value, # always present, even on error
"exec_out": True
}
Values not updating between runs
Symptom: A node seems to remember values from the previous run.
Cause: State is stored in self.parameters (persisted to the workflow JSON) instead of self.memory (cleared each run).
Fix: Use self.memory for per-run transient state:
# Wrong: persists across runs
self.parameters["count"] = self.parameters.get("count", 0) + 1
# Correct: fresh each run
count = self.memory.get("count", 0) + 1
self.memory["count"] = count
6. Houdini Bridge Not Connecting
ConnectionRefusedError or ConnectionError: Houdini bridge not connected
Symptom: Any Houdini node fails with a connection error.
Checklist:
-
Is the Vibrante-Node server running inside Houdini?
In Houdini, open the Python Shell and run:
python import vibrante_hou_server print(vibrante_hou_server._server_thread)
If it printsNone, the server is not running. Start it:
python vibrante_hou_server.start() -
Is the port correct?
Default port is18811. Check:
python import os print(os.environ.get("VIBRANTE_HOU_PORT", "18811 (default)"))
In Houdini:
python print(vibrante_hou_server._port)
Both must match. -
Is a firewall blocking localhost?
The bridge uses127.0.0.1(loopback). Windows Defender or corporate firewalls may block local socket connections. Add an exception for Python on localhost port 18811. -
Did Houdini launch the app, or did you launch it manually?
VIBRANTE_HOU_PORTis only set in the subprocess environment when launched from Houdini via Vibrante-Node → Launch. Runningpython -m src.maindirectly will use the default port, which only works if the Houdini server is also on the default port.
30-second timeout then ConnectionError
Symptom: A Houdini node hangs for 30 seconds then fails with ConnectionError: Houdini did not respond within 30s.
Cause: Houdini is busy (e.g., cooking a heavy SOP network) and cannot respond to the JSON-RPC server.
Fix:
- Wait for the Houdini cook to finish before running Vibrante-Node.
- For long-running operations, consider using bridge.run_code() to start a background cook and poll for completion, rather than calling bridge.cook_node() synchronously.
7. Maya Headless Failing
FileNotFoundError for Maya executable
Symptom: Maya headless executor fails immediately with a path error.
Fix: Set VIBRANTE_MAYA_EXE in your environment to the full path of mayapy.exe:
# Windows
set VIBRANTE_MAYA_EXE=C:\Program Files\Autodesk\Maya2024\bin\mayapy.exe
# macOS
export VIBRANTE_MAYA_EXE=/Applications/Autodesk/maya2024/Maya.app/Contents/bin/mayapy
Scene not found error
Symptom: open_scene action fails with "file not found" even though the path looks correct.
Cause: The path may contain backslashes that Maya's Python interpreter interprets incorrectly, or the file doesn't exist at the given path from the Maya subprocess's working directory.
Fix: Always use forward slashes in scene paths:
scene_path = scene_path.replace("\\", "/")
Action handler missing
Symptom: KeyError: 'my_action_type' or Unknown action type: my_action_type in the Maya runner log.
Cause: The action type string in the action dict does not have a corresponding handler in the Maya runner script.
Fix: Open plugins/maya/maya_runner.py (or equivalent) and add:
def handle_my_action_type(action: dict, scene_info: dict):
# Implementation
pass
HANDLERS = {
# ... existing handlers ...
"my_action_type": handle_my_action_type,
}
8. Prism Nodes Not Working
PrismCore not initialized
Symptom: All prism_* nodes log "PrismCore not initialized. Add a prism_core_init node to the graph." and produce empty output.
Cause: The prism_core_init node is missing from the workflow graph.
Fix: Drag a prism_core_init node onto the canvas. It does not need to be wired into the exec chain — the engine detects its presence before execution and bootstraps PrismCore automatically. However, it must be present (not bypassed).
Prism import error
Symptom: ModuleNotFoundError: No module named 'PrismCore' or No module named 'Prism_Core'.
Cause: Prism Pipeline is not installed, or its Python path is not set.
Fix: Ensure the Prism Python scripts directory is in sys.path. Check your Prism installation for the correct path and add:
# In src/utils/prism_core.py or your launch script
sys.path.insert(0, "/path/to/Prism2/Scripts")
9. GroupNode Issues
"No internal workflow" error
Symptom: Double-clicking a GroupNode shows an error: "This group node has no internal workflow."
Cause: The GroupNode's __workflow__ parameter is empty or was not saved correctly. This was a known issue in early versions caused by UUID serialization — fixed in v2.0.0.
Diagnosis: Right-click the GroupNode → Inspect Parameters. Check if __workflow__ contains a valid workflow dict or is empty/null.
Fix: If the GroupNode is empty and you still have the original nodes in another tab or workflow:
1. Re-create the subgraph in a new tab.
2. Select those nodes.
3. Ctrl+Shift+G to create a fresh GroupNode.
4. The new GroupNode will serialize correctly.
If upgrading from a pre-v2.0.0 workflow with broken GroupNodes, the only recovery path is to re-create the groups. The bug was in serialization, so the internal logic cannot be recovered from the corrupt file.
Exec flow not propagating out of GroupNode
Symptom: Nodes downstream of a GroupNode never execute.
Cause: The GroupNode's internal GroupOut node is not connected in the exec chain, or GroupOut is not wired to exec_in.
Fix: Double-click the GroupNode to open the subgraph. Verify that the exec chain inside runs through to a GroupOut node. GroupOut must have exec_in connected from the last node in the inner chain.
Also verify GroupOut's port_name matches the expected output port name on the outer GroupNode.
10. Exec Flow Not Progressing
Workflow stops mid-execution silently
Symptom: Some nodes run, then execution stops with no error.
Cause: A node returned a dict without "exec_out": True, or returned exec_out: False.
Diagnosis:
1. Check the log panel — the last node listed is where execution stopped.
2. Open that node's execute() code and check every return statement.
Fix: Ensure every code path returns "exec_out": True:
async def execute(self, inputs):
if something_failed:
self.log_error("Something failed")
return {"result": None, "exec_out": True} # exec_out still True — chain continues
return {"result": value, "exec_out": True}
If you want to intentionally stop the exec chain (rare), return "exec_out": False.
TwoWaySwitch not routing correctly
Symptom: TwoWaySwitch always routes to exec_true even when condition should be False.
Cause: The condition input is receiving a truthy non-boolean value (e.g., the string "False" is truthy in Python).
Fix: Ensure the condition value is a Python bool. In a Python Script node:
# Wrong: string "False" is truthy
result = "False"
# Correct
result = False
result = some_value > threshold # bool expression
result = bool(int(inputs.get("data", 0))) # explicit cast
11. Autosave Restore Dialog Always Showing
Symptom: Every time the app starts, the "Restore unsaved sessions?" dialog appears, even after accepting or declining.
Cause: The autosave file (~/.vibrante_node_autosave.json) is being written but not deleted on clean exit. This happens if the app is force-closed, crashes, or if closeEvent is not being called.
Manual fix: Delete the autosave file:
# Windows
del %USERPROFILE%\.vibrante_node_autosave.json
# Linux / macOS
rm ~/.vibrante_node_autosave.json
Permanent fix: Ensure you close the app via the window's X button or File → Exit, not via Task Manager or kill. The close event cleans up the autosave file on normal exit.
If the dialog appears after a crash: this is by design. Accept the restore to recover unsaved work, or decline to start fresh.
12. Async Deadlock / UI Freezing
Symptom: The UI becomes unresponsive during execution. The progress indicator spins but nothing moves.
Cause: A blocking call inside execute() is holding the event loop:
- time.sleep() — freezes the event loop thread
- requests.get() — synchronous HTTP blocks
- subprocess.run(capture_output=True) without timeout — hangs waiting for subprocess
Fix: Wrap blocking calls in asyncio.to_thread():
import asyncio
async def execute(self, inputs):
# Wrong: blocks the event loop
import time
time.sleep(5)
# Correct: offloads to a thread, event loop stays responsive
await asyncio.sleep(5) # for delays
# Wrong: synchronous HTTP
import requests
response = requests.get(url)
# Correct: async HTTP
import aiohttp
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
data = await response.json()
# Wrong: blocking subprocess
import subprocess
result = subprocess.run(["cmd"], capture_output=True)
# Correct: async subprocess
proc = await asyncio.create_subprocess_exec(
"cmd", stdout=asyncio.subprocess.PIPE
)
stdout, _ = await proc.communicate()
13. Memory Growing Over Multiple Runs
Symptom: The app uses more RAM each time you click Run.
Cause 1: Storing large data in self.parameters
self.parameters is persisted in the workflow JSON. If you store large binary data or giant lists there, they accumulate and are serialized to disk on save.
Fix: Never store large transient data in self.parameters. Use self.memory for per-run state — it is cleared before each run.
Cause 2: Circular references in the graph's node_results
If nodes hold references to large objects and those objects reference back to the node, Python's garbage collector may not reclaim them.
Fix: In execute(), return primitive types (strings, numbers, lists of primitives) where possible. Avoid returning the entire input dict as an output.
14. PyInstaller Build Fails
Symptom: ModuleNotFoundError at runtime when using the built .exe, even though the app works fine when running from source.
Cause: PyInstaller's static analysis misses dynamically-imported modules (like node classes loaded from JSON files at runtime).
Fix — add hidden imports to vibrante_node.spec:
hiddenimports=[
"src.nodes.builtins.group_node",
"src.nodes.builtins.my_new_node",
"src.utils.hou_bridge",
"src.utils.prism_core",
"PyQt5.sip",
"pydantic.v1.validators", # if using pydantic v2 compatibility layer
],
Fix — include data files:
datas=[
("nodes/", "nodes/"),
("icons/", "icons/"),
("plugins/", "plugins/"),
],
After updating the spec file:
pyinstaller vibrante_node.spec --clean
15. Reading crash.log
When the app crashes before the UI is ready, a crash.log file is written to the project root. Its format:
=== Vibrante-Node Crash Log ===
Version: 2.0.0
Time: 2026-05-10 14:32:01
Python: 3.11.4
Platform: win32
Traceback (most recent call last):
File "src/main.py", line 42, in main
window = MainWindow()
File "src/ui/window.py", line 88, in __init__
self._load_nodes()
...
SyntaxError: invalid syntax (<string>, line 7)
Common crash causes from crash.log:
| Error in log | Cause | Fix |
|---|---|---|
SyntaxError in <string> |
Broken python_code in a JSON node |
Fix the node JSON in nodes/ |
ValidationError from pydantic |
Corrupt settings.json |
Delete settings file |
ImportError: No module named 'X' |
Missing dependency | pip install X |
FileNotFoundError: icons/X.svg |
Node JSON references a missing icon | Add the icon or set "icon_path": null |
AttributeError: 'NoneType' in _init_menu |
Settings returned None for a required value | Delete settings file |
16. Performance: Too Many Nodes Causing Slowness
Symptom: The canvas becomes sluggish with 100+ nodes. Workflow execution is slower than expected.
Canvas performance:
- Reduce the number of visible nodes by collapsing related nodes into GroupNodes (Ctrl+Shift+G).
- Disable the mini-map (Ctrl+M) if you have a large number of nodes — the mini-map re-renders the full scene.
- Use backdrop nodes to organize without adding execution overhead.
Execution performance:
- Identify slow nodes using the timing log: Node 'X' finished in 12.34s.
- Move heavy I/O operations to asyncio.to_thread() so they run concurrently.
- For batch operations, prefer a single Python Script node that processes a list internally, rather than a ForEach loop with many individual nodes — the loop overhead adds up.
- Cache results using SetVariable / GetVariable rather than re-computing on every iteration.
Profiling a slow workflow:
# Temporary: add to a Python Script node to profile a specific operation
import cProfile
import pstats
import io
pr = cProfile.Profile()
pr.enable()
# ... your code here ...
pr.disable()
s = io.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats("cumulative")
ps.print_stats(20)
self.log_info(s.getvalue())