Troubleshooting
A complete guide to diagnosing and resolving errors in the StoryFlow Godot plugin. Covers every runtime error message, debugging techniques, and common solutions.
Error Handling Overview
The StoryFlow plugin reports errors through two channels:
error_occurred Signal
A signal on StoryFlowComponent. Connect to this in GDScript to handle errors in your game - for example, showing a message to the player or logging to your own system.
Output Panel
All errors are also logged via push_warning() and push_error(). Open the Output panel in the Godot Editor to see all plugin messages alongside your own prints.
Errors Are Non-Fatal
StoryFlow errors do not crash your game. When an error occurs, the runtime stops processing the current node chain and emits error_occurred. Dialogue execution may end or stall, but your game continues running. This lets you handle errors gracefully in your UI.
Listening for Errors
To respond to errors at runtime, connect to the error_occurred signal on your StoryFlowComponent.
# In your scene script's _ready()
func _ready() -> void:
var story_flow = $StoryFlowComponent
story_flow.error_occurred.connect(_on_storyflow_error)
# Handler function
func _on_storyflow_error(error_message: String) -> void:
push_warning("StoryFlow error: " + error_message)
# Show error in your UI, log to analytics, etc.
# Optionally stop dialogue to prevent broken UI state
if story_flow.is_dialogue_active():
story_flow.stop_dialogue()
You can also connect the signal in the Godot Editor via the Node panel's Signals tab. Select the StoryFlowComponent node, find error_occurred(message: String), and connect it to a handler method on your script.
Initialization Errors
These errors occur when calling start_dialogue() or start_dialogue_with_script() before the component is properly configured.
| Error Message | Cause | Solution |
|---|---|---|
No script configured for StoryFlowComponent | Called start_dialogue() without setting the script_path property. | Set the script_path property in the Inspector panel or use start_dialogue_with_script() instead. |
start_dialogue_with_script called with empty path | Passed an empty string to start_dialogue_with_script(). | Provide a valid script path (e.g., "scripts/main"). |
StoryFlowRuntime autoload not found | The StoryFlowRuntime autoload hasn't initialized. This can happen if the plugin is not enabled or called too early in the lifecycle. | Ensure the plugin is enabled in Project > Project Settings > Plugins. Call start_dialogue() after _ready(). |
No StoryFlow project loaded. Import a project or set it via StoryFlowRuntime. | No project has been imported or set on the manager. | Import your StoryFlow project via the importer or call set_project() on the manager. |
Script not found: {path} | The script_path property references a script path that doesn't exist in the imported project. | Check the script path matches one of the imported scripts. Use get_manager().get_all_script_paths() to list available scripts. |
Start node (id=0) not found in script | The script data doesn't contain a Start node with id "0". The script may be corrupted or was exported incorrectly. | Re-export the project from StoryFlow Editor and re-import into Godot. Verify the script has a Start node in the editor. |
Graph Execution Errors
These errors occur during node graph traversal when the runtime encounters invalid connections or structural problems.
| Error Message | Cause | Solution |
|---|---|---|
Target node not found: {id} | An edge references a node ID that doesn't exist in the script. The target node may have been deleted after the connection was created. | Open the script in StoryFlow Editor and fix any broken connections. Re-export and re-import the project. |
Max processing depth exceeded ({depth}) - possible cyclic graph | The node processing loop has exceeded the safety limit (1000). This happens when non-dialogue nodes form a cycle with no exit condition. | Check for loops in your non-dialogue node chains. Make sure every loop has a Branch node that eventually exits the cycle. |
Script & Flow Errors
These errors occur when using runScript or runFlow nodes to call other scripts or flows.
| Error Message | Cause | Solution |
|---|---|---|
RunScript node has no script path | A runScript node has no script path configured. | Open the script in StoryFlow Editor and set a valid script path on the Run Script node. |
Max script nesting depth exceeded ({depth}) | Scripts calling other scripts have exceeded the maximum nesting depth (20). This usually indicates a circular dependency. | Check your Run Script nodes for circular references. Reorganize your script hierarchy to avoid loops. |
RunFlow node has no flow ID | A runFlow node has no flow selected. | Open the script in StoryFlow Editor and configure the Run Flow node with a valid flow. |
Too many nested flows - possible infinite loop | Flows calling other flows have exceeded the maximum nesting depth (50). | Check your Run Flow nodes for circular references. Make sure flows don't call themselves or form a loop. |
EntryFlow not found for flowId: {id} | A Run Flow node references a flow that doesn't exist in the script. | Verify the flow exists in the script. The flow may have been renamed or deleted in the editor. |
Forward-Compatibility Warnings
The plugin treats unknown node types as a forward-compatibility concern rather than a fatal error. When the runtime encounters a node whose type the installed plugin version does not recognize, it logs a warning and continues. These warnings are emitted via push_warning() only and do not fire the error_occurred signal, so listeners wired up for errors will not see them.
| Warning Message | Cause | Solution |
|---|---|---|
StoryFlow: Unsupported node type '{type}' at node {id}, skipping | The dispatcher (_process_node) hit a node type the plugin does not have a handler for. Execution falls through to the node's default output edge so newer scripts do not freeze on older plugin builds. | Update the StoryFlow Godot plugin to a build that supports the node type, or remove the unsupported node from the script in the StoryFlow Editor and re-export. |
StoryFlow: Unsupported node type '{type}' at node {id}, returning default value | An evaluator chain (boolean, conversion, comparison logic) followed an input edge into a node of an unknown type. The evaluator returns the type's default value and continues. Deduplicated to fire once per node id per dialogue run. | Same as above. Update the plugin or replace the unsupported node with one the plugin recognizes. |
Warnings, not errors
These messages indicate that the plugin runtime predates a node type used in the script. Dialogue continues running with a best-effort fallback, so the player experience degrades gracefully rather than crashing. Treat the warning as a signal to update the plugin or simplify the script.
A concrete case: unsupported-node warnings naming the map node types (getMap, setMap, getMapValue, setMapValue, hasMapKey, mapSize, mapKeys, mapValues, removeMapKey, clearMap, forEachMap) or the arithmetic nodes modulo and moduloFloat mean the installed plugin predates v1.2.0, which introduced those node types. Update the plugin to 1.2.0 or newer to resolve them.
Import and Sync Errors
These come from the importer rather than from graph execution, so they appear in the Output panel and never fire error_occurred. Since v1.2.2 every failed write below is counted, and the dock's result line appends N errors. Check Output log. instead of announcing a clean sync. The missing-media warning in the first row is not a write failure, so it never adds to that number. The same total is available as get_error_count() on StoryFlowImporter and as the second argument of sync_complete.
| Message | Cause | Solution |
|---|---|---|
StoryFlow: Source media file not found: {path} | The importer could not read the raw bytes of a media file and could not reach an imported version of it either. In an exported game this used to mean media silently went missing, because exports pack only Godot's imported resource and never the raw file. | Since v1.2.2 media falls back to ResourceLoader through Godot's path remap, so a surviving warning means the asset is neither in the build nor imported into the Godot project. Run a full sync (not a data-only one) so the file is copied in, and confirm it exists in your StoryFlow project. |
StoryFlow: Refusing to copy {src} -> {dst}: the directories are nested, which would recurse without end | The output directory sits inside the build directory, so copying the build into the output would descend into its own output forever. Before v1.2.2 this filled the disk instead of failing. | Move the output directory outside the build directory. Everything beside the nested directory is still copied, so the rest of the import completes. |
StoryFlow: Failed to copy {src} -> {dst} | A file could not be written into the output directory: permissions, a read-only location, a locked file or a full disk. | Check that the output directory is writable and that no other process holds the destination file open. The project still imports into memory, but the copy on disk is stale or missing. |
StoryFlow: Failed to create directory {path} | A destination subdirectory could not be created, so nothing underneath it was copied. | Same as above: verify the output path exists, is writable and is not inside a read-only location. |
StoryFlow: Cannot stage import metadata {path} / StoryFlow: Failed to publish import metadata {path} | storyflow_import_meta.json is written to a temp file and renamed over the target. One of those two steps failed, so the metadata was not updated. | The previously published metadata is left intact, so an existing project keeps loading. Fix the write permissions on the output directory and sync again. Without valid metadata the runtime cannot auto-discover the project on the next launch. |
Fixed in v1.2.2
If you hit either of these, the fix is to update the plugin to v1.2.2 or newer. Nothing needs to change in your project or your scripts.
| Symptom | Cause |
|---|---|
Parser Error: Could not find type "StoryFlowProject" in the current scope at storyflow_manager.gd:9 when launching a scene, which kills the StoryFlowRuntime autoload for that run. | Global class name resolution in a running game reads a cache file the editor can be rewriting at the moment the game process launches, so the failure is intermittent. Every script the game parses at runtime now resolves its sibling classes by path instead of through that cache. class_name declarations are unchanged, so your game code keeps using the global names. |
{charVar.Name} printing verbatim in dialogue text, while the StoryFlow Editor runtime resolved it correctly. | Reaching through a character-type variable to a field on the character it points to was not resolved by the plugin. Character-type variables now resolve their inner fields, for parity with the editor runtime. |
Depth Limits
The plugin enforces depth limits at multiple levels to prevent infinite recursion from freezing your game.
| Resource | Maximum Depth | Description |
|---|---|---|
| Script nesting | 20 | Maximum runScript calls deep before the runtime stops |
| Flow nesting | 50 | Maximum runFlow calls deep before the runtime stops |
| Evaluation depth | 100 | Maximum node evaluation chain depth (nested boolean/conversion logic) |
| Processing depth | 1000 | Maximum consecutive non-dialogue nodes processed before the runtime stops |
Debugging Strategies
Check the Output Panel
All StoryFlow plugin messages are printed to Godot's Output panel via push_warning() and push_error(). When something goes wrong, the Output panel is the first place to look. Error messages include the specific node ID or script path involved, making it easy to pinpoint the source of the problem.
Track Variable Changes
Connect to the variable_changed signal to monitor variable state during execution. This is especially useful for debugging Branch nodes that always take the same path - you can verify the boolean variable has the expected value at the moment the Branch node evaluates.
func _ready() -> void:
story_flow_component.variable_changed.connect(_on_variable_changed)
func _on_variable_changed(info: StoryFlowVariableChangeInfo) -> void:
print("Variable '%s' (id: %s) changed to '%s' (global: %s)" % [
info.name,
info.id,
info.value.to_display_string(),
str(info.is_global)
]) Use Live Sync for Rapid Iteration
When debugging, use Live Sync to push changes from StoryFlow Editor to Godot without re-importing manually. This lets you fix a broken connection in the editor and immediately test the fix in-game.
Cross-Reference with the Editor
Many plugin errors mirror editor runtime errors. If you see an error in Godot, try playing the same script in StoryFlow Editor's Play Window to reproduce it. The editor's Runtime Debugger provides a visual, step-by-step view of execution that can pinpoint the exact node causing the problem.
Editor Errors Page
For a complete list of editor-side error messages and their solutions, see the Errors & Troubleshooting page in the editor documentation. Many errors are shared between the editor runtime and the Godot plugin.