Skip to main content

Troubleshooting

A complete guide to diagnosing and resolving errors in the StoryFlow Unity plugin. Covers error messages, common pitfalls, and debugging strategies.

Errors Are Non-Fatal

StoryFlow errors do not crash your game. When an error occurs, the runtime stops processing the current node chain and fires the OnError event. Dialogue execution may end or stall, but your game continues running. This lets you handle errors gracefully in your UI.

Common Errors

The following table lists the most common error messages you may encounter, along with their causes and solutions.

Error Cause Solution
Cannot start dialogue: no StoryFlow project found. Import a project via Tools > StoryFlow > Import Project. In the Unity editor: no project has been imported yet. In a built game on plugin 1.2.1 or older: the project asset was imported but nothing anchored it into the build, so Unity stripped it. Play mode still works because the editor uses a fallback that does not exist in builds. Editor: import a project via Tools > StoryFlow > Import Project (top-level StoryFlow menu before 1.2.2). Builds: update to plugin 1.2.2+ and re-import or re-sync once; the importer then assigns the project in Edit > Project Settings > StoryFlow automatically. On 1.2.1, add a StoryFlowManager to the first scene of your build and assign its Project field.
Cannot start dialogue: no StoryFlow project found in this build. Shown by plugin 1.2.2+ in built games when no build-visible project anchor exists: StoryFlowSettings.DefaultProject is unassigned, no scene in the build references the project asset and game code never called SetProject(). Open Edit > Project Settings > StoryFlow and assign Project Asset, or simply re-import your project (the importer assigns it automatically), then rebuild. The build log also warns about this ahead of time.
Cannot start dialogue: no script assigned and no startup script in project. Called StartDialogue() without assigning a Script asset on the component, and the project has no startup script configured. Assign a StoryFlowScriptAsset to the Script field in the Inspector, or use StartDialogue(scriptPath) to specify a script path at runtime.
Cannot start dialogue: script not found at path "{path}". The specified script path does not match any script in the imported project. Verify the script path is correct. Check StoryFlowManager.Instance.GetAllScriptPaths() for available paths. Re-import the project if scripts were added recently.
Cannot load save while dialogue is active. Stop all dialogues before loading. LoadFromSlot() was called while one or more StoryFlowComponent instances have active dialogues. Call StopDialogue() on all active components before loading. Check StoryFlowManager.Instance.IsDialogueActive() first.
Processing depth exceeded {depth}. Possible infinite loop detected. Stopping dialogue. Non-dialogue nodes form a cycle with no exit condition, causing the runtime to loop indefinitely. The default limit is 1000 iterations. Check for loops in your node chains. Ensure every loop has a Branch node that eventually exits the cycle.
Call stack overflow: too many nested script calls. RunScript nodes have formed a recursive chain exceeding the maximum depth (20 levels). Check for circular script references (Script A calls Script B, which calls Script A). Reorganize your script hierarchy.
Flow stack overflow: too many nested flow calls. RunFlow nodes have formed a recursive chain exceeding the maximum depth (50 levels). Check for circular flow references. Flows should not call themselves or form loops.
SetCharacterVar: character not found for path '{path}' The character path used in a character variable node does not match any imported character. Paths are normalized to lowercase with forward slashes. Ensure the character path matches the normalized format. Character paths are lowercased and use forward slashes internally. Re-import the project to ensure characters are present.
Script not found: {scriptId} A RunScript node references a script that does not exist in the project. The script may have been renamed or deleted. Re-export and re-import the project to ensure all scripts are present. Check the script path in your node graph.
Dialogue nodes cannot be used inside ForEach loops. A dialogue node was placed inside a ForEach loop body, which is not supported. Move the dialogue node outside the ForEach loop. Use variables to collect data inside the loop and display it in a dialogue after the loop completes.

Import Issues

Files Reported as Failed

The importer never aborts on a single unwritable file. Each media copy and each asset save is attempted on its own, a file that cannot be written is skipped and the run carries on with the next one. The skipped file is retried on the next sync.

Failures are reported per file rather than as one opaque exception. Every caller checks the report before claiming success, so the Live Sync log, the import window's status box and the project asset's Re-Import dialog all say how many files were written, were already up to date and failed. Alongside them the importer writes a single Console error naming every file it could not write, with the likely cause for each:

  • Read-only or checked in - the usual case under Perforce or Plastic, which mark unopened files read-only. Check the file out, or clear its read-only attribute, then sync again.
  • Path too long - a deeply nested build directory plus a long exported asset path clears Windows' 260-character limit. Import to a shorter output folder, or move the project closer to the drive root.
  • Open in another application - something else is holding the file. Close it and sync again. The same message covers a destination drive that is out of space.

A media file is counted once however many scripts reference it, so the counts match the number of files you actually need to fix rather than the number of times the importer reached them.

Refused asset paths

An exported asset path containing a .. segment would resolve out of the output folder and land a raw file copy somewhere nothing in the project points at, so the importer refuses it. The path is reported by name as an invalid-path failure and the rest of the import continues. Re-export the project, or move the file inside the build directory, then sync again.

Version Control (Perforce and Plastic)

Unity checks out anything it writes through the AssetDatabase, but the media the importer copies goes through a raw file copy and bypasses that, so the checkout has to be asked for explicitly. With a provider configured and connected:

  • An existing media file is checked out with AssetDatabase.MakeEditable before it is overwritten, and an asset is made writable before it is marked dirty. Settling writability first matters: an asset left dirty behind a refusal would be written by the next flush that comes along, including editor shutdown.
  • A refused checkout fails that one file with a message saying so. Everything else in the import still goes through.
  • A media file the import created is marked for add afterwards, so it does not sit unversioned next to versioned siblings. A failed add is only a warning, never an import failure.

With no provider configured, the importer refuses a read-only file rather than clearing the attribute. A read-only file in a Unity project is nearly always owned by a version control system Unity is not driving, including a provider that is configured but offline, and overwriting it loses the change at the next sync or revert. Check the file out or clear the attribute yourself, then sync again.

Unchanged media is compared by content before any of this happens, so a routine sync generates no checkout traffic at all.

Missing Media Files

If images or audio files referenced in your project are not appearing after import:

  • Check the build/media/ directory - The importer reads media files from the build/media/ folder inside the exported project. If files are missing there, re-export from the StoryFlow Editor with media included.
  • Verify file formats - Unity imports standard image formats (PNG, JPG) and audio formats (WAV, OGG). MP3 files are supported but WAV is recommended for best compatibility.
  • Check the Console - The importer logs warnings for any files it cannot find or import. Look for [StoryFlow] messages.

Newtonsoft.Json Errors

If you see errors related to Newtonsoft.Json or JsonConvert:

  • Install the dependency - The StoryFlow package requires com.unity.nuget.newtonsoft-json. This should be installed automatically by the Unity Package Manager when you add StoryFlow. If it was not, install it manually via Window > Package Manager > + > Add package by name and enter com.unity.nuget.newtonsoft-json.
  • Version conflicts - If your project already uses a different version of Newtonsoft.Json (e.g., from a third-party asset), you may see version mismatch errors. Ensure only one version is installed.

IL2CPP Stripping Issues

When building for platforms that use IL2CPP (iOS, WebGL, some Android builds), the code stripping process may remove types needed for JSON deserialization at runtime.

link.xml Protection

The StoryFlow package includes a link.xml file in the Runtime/ folder that prevents IL2CPP from stripping essential types. If you encounter TypeLoadException or JSON deserialization failures in IL2CPP builds, verify that the link.xml file is present in the package's Runtime/ directory. If you have moved the package files manually, ensure link.xml is included in your build.

Runtime Issues

Variables Not Updating

If variable values do not seem to change when you expect them to:

  • Check the scope parameter - The Get/SetXxxVariable methods accept a global parameter. When global is false (default), the method searches local variables first, then global. If you need to target a specific global variable, pass global: true.
  • Local variables reset per script - Local variables are scoped to the current script. When execution enters a new script via RunScript, local variables are re-initialized. Global variables persist across all scripts.
  • Variable names are case-sensitive - The name parameter must match the variable's display name exactly as defined in the StoryFlow Editor.
C#
// Reading a global variable
bool isQuestComplete = storyFlow.GetBoolVariable("QuestComplete", global: true);

// Reading a local variable (searches local first, then global)
int dialogueCounter = storyFlow.GetIntVariable("counter");

Dialogue Not Starting

If StartDialogue() is called but nothing happens:

  • Check for errors - Subscribe to the OnError event to catch initialization errors. The most common cause is a missing project or invalid script path.
  • Verify the manager exists - Ensure a StoryFlowManager is in the scene and has been initialized. If using DontDestroyOnLoad, make sure the manager's scene is loaded first.
  • Check the script has a Start node - Every script must have a Start node with ID "0". If the script was corrupted or exported incorrectly, re-export from the editor.
  • Verify the component is enabled - A disabled component or a disabled GameObject will not process dialogue. Check gameObject.activeInHierarchy and enabled.
  • Check the build log - Building a player runs a preprocess check that warns Build check: this project contains StoryFlow project assets, but none is anchored for player builds when nothing will make the project discoverable at runtime. It only warns and never fails the build, since a scene reference also works and cannot be verified cheaply. Fix it by assigning Default Project in Edit > Project Settings > StoryFlow, or by re-importing, which assigns it automatically.
  • Check the Console - [StoryFlow] prefixed warning and error messages are logged for all failure cases.

Audio Not Playing

If dialogue audio is not playing when expected:

  • Check the audio file format - Unity supports WAV, OGG, and MP3. WAV is recommended for best compatibility. Ensure audio files were included in the project export.
  • Check the AudioMixerGroup - If DialogueAudioMixerGroup is assigned, ensure the mixer group is not muted and the volume is not set to zero.
  • Check the volume multiplier - DialogueVolumeMultiplier defaults to 1.0 but may have been set to 0 in the Inspector.
  • Audio requires fresh entry - Audio only plays when the dialogue node is entered for the first time, not when returning to it from a Set* node (which re-renders the dialogue text without replaying audio).
  • Check StopAudioOnDialogueEnd - If this is true (default), audio stops when the dialogue ends. If you expect audio to outlast the dialogue, set this to false.

Unsupported Node Type Warnings

The runtime emits two distinct warnings when it encounters a node type it does not recognize. Both indicate that a script authored in a newer StoryFlow Editor is running against an older plugin build that lacks a handler for some node type.

[StoryFlow] Unsupported node type '<typeName>' at node <id>, skipping comes from StoryFlowNodeDispatcher. This is a behavioral fix worth knowing about. Previously these nodes were silent no-ops and the dialogue froze on them. The runtime now follows the default outgoing edge so newer scripts gracefully skip past unknown nodes on older plugin builds, and the warning makes the skip visible in the Console. If you see this consistently, update the plugin to a version that supports the node type. For custom nodes you author in your own code, register a handler with StoryFlowNodeDispatcher.RegisterCustomHandler.

[StoryFlow] Unsupported node type '<rawType>' at node <id>, returning default value comes from StoryFlowExecutionContext. It is emitted once per node id per dialogue run (via the warnedUnknownNodes dedup set, so a repeated visit to the same node will not spam the Console). This warning indicates an evaluator chain feeding through an unknown node. The default value is returned in place of the real result, which usually means a downstream branch will take the wrong path. Update the plugin or register a custom evaluator if your script depends on the affected node.

A concrete case: if the warnings name getMap, setMap, forEachMap (or any other map node), modulo or moduloFloat, the plugin predates 1.2.0. Map variables and modulo nodes were added in v1.2.0, so update the plugin to run scripts that use them.

UI Issues

Events Not Firing

If your UI is not receiving dialogue updates:

  • Subscribe before starting - Ensure you subscribe to events (e.g., OnDialogueUpdated) before calling StartDialogue(). If you subscribe after the dialogue has already started, you will miss the initial state update.
  • Check the DialogueUI field - If you are using a StoryFlowDialogueUI subclass, assign it to the component's DialogueUI field in the Inspector. The binding happens automatically when dialogue starts, but only if the reference is set.
  • Manual binding - If you are not using the Inspector field, call dialogueUI.InitializeWithComponent(storyFlowComponent) before starting the dialogue.
C#
// Ensure events are subscribed before starting dialogue
void Start()
{
    var storyFlow = GetComponent<StoryFlowComponent>();

    // Subscribe to events FIRST
    storyFlow.OnDialogueStarted += HandleDialogueStarted;
    storyFlow.OnDialogueUpdated += HandleDialogueUpdated;
    storyFlow.OnDialogueEnded += HandleDialogueEnded;
    storyFlow.OnError += HandleError;

    // THEN start dialogue
    storyFlow.StartDialogue();
}

Options Not Showing

If dialogue options are not appearing in your UI:

  • Check IsOnceOnly - Once-only options disappear after being selected. If all options in a dialogue node are once-only and have been used, the options list will be empty. Check state.Options.Count and option.IsSelected.
  • Check conditional visibility - Options may be hidden by branch conditions in the StoryFlow Editor. If an option's visibility condition evaluates to false, it will not appear in the Options list.
  • Check CanAdvance - If the dialogue state has CanAdvance == true and Options.Count == 0, the dialogue is in "continue" mode (narrative text with no choices). Show a continue button instead of options.

Debugging

Enable Trace Logging (per-component)

Each StoryFlowComponent has a TraceEnabled field (Inspector → Debugging section, on by default) that emits [SF-TRACE] lines for every node processed, edge traversed, variable change, and script call. The trace format is identical across the Unreal, Godot, and Unity plugins, so a misbehavior reproduced in Unity can be diffed against a trace from the StoryFlow Editor's runtime debugger to pinpoint where the runtimes diverge. Disable this in release builds to reduce log volume.

Enable Verbose Logging (project-wide)

For older, non-trace-format diagnostics the plugin also exposes two project-wide logging options on StoryFlowSettings:

  • VerboseLogging - Logs every node that is processed during execution. This is the older, human-readable counterpart to TraceEnabled above. If both are on, you will see two log lines per node (one plain, one with [SF-TRACE]).
  • LogVariableChanges - Logs every variable change during execution, including the variable name, new value, and whether it is local or global. Useful for debugging Branch nodes that always take the wrong path.

Both toggles live on the StoryFlowSettings asset at Assets/Resources/StoryFlowSettings.asset. There is no Assets > Create entry for it. Open Edit > Project Settings > StoryFlow, which creates the asset if it is missing and exposes both toggles under Debug, or import a project, which creates the same asset as part of the import.

Subscribe to OnError

Always subscribe to the OnError event during development. This catches runtime errors that might otherwise go unnoticed if you are not watching the Console.

C#
var storyFlow = GetComponent<StoryFlowComponent>();

storyFlow.OnError += (message) =>
{
    Debug.LogError($"[StoryFlow Error] {message}");

    // Optionally show in-game error UI during development
    #if UNITY_EDITOR || DEVELOPMENT_BUILD
    ShowDebugOverlay(message);
    #endif
};

Track Variable Changes

Subscribe to OnVariableChanged to monitor variable state during execution. This is especially useful when debugging Branch nodes that always take the same path:

C#
storyFlow.OnVariableChanged += (variable, isGlobal) =>
{
    string scope = isGlobal ? "global" : "local";
    Debug.Log($"[StoryFlow] Variable changed [{scope}]: " +
              $""{variable.Name}" = {variable.Value} (type: {variable.Type})");
};

Check Unity Console

All StoryFlow plugin messages use the [StoryFlow] prefix. In the Unity Console window, use the search box to filter for "StoryFlow" to see only plugin-related messages. This includes:

  • Initialization messages when the manager loads a project
  • Import progress and results
  • Node execution logs (when verbose logging is enabled)
  • Variable change logs (when variable logging is enabled)
  • Warnings for missing assets, null references, or invalid state
  • Error messages for execution failures

Cross-Reference with the Editor

Many plugin errors mirror editor runtime errors. If you see an error in Unity, try playing the same script in the 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.

Use Live Sync for Rapid Iteration

When debugging, use Live Sync to push changes from the StoryFlow Editor to Unity without manually re-exporting. Fix a broken connection or update variable logic in the editor, save, and immediately test the fix in Unity.

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 Unity plugin.

Need Help?

Join our Discord community to ask questions, share your projects, report bugs, and get support from the team and other users.

Join Discord