Skip to content

Troubleshooting

Most failures are refusals, not crashes: threadle preflights a run and stops before spending money. The message names the node.

ProblemCauseFix
No sessions listed for a providerIts storage directory does not exist where threadle looksInstall the tool, or point threadle at the right root with CLAUDE_CONFIG_DIR / OPENCODE_DATA_DIR / CURSOR_CONFIG_DIR / ANTIGRAVITY_HOME / CODEX_HOME / COPILOT_HOME / GROK_HOME. Confirm with threadle check
!! flag:… from threadle check --providersAn installed agent CLI’s help no longer mentions an inject token threadle probes forConfirm with that CLI’s help (including subcommands). If the flag still works but help wording changed, update the probe list. See CLI → Check and Providers → Keeping adapters fresh
detached run blocked — fix (or mute) node(s) first: …One or more readiness blockersFix the named nodes, or mute them
model not set — pick one in the inspectorAn agent node has no modelPick a model in the inspector. Detached runs cannot ask
Agent failed and the whole run stoppedout:err is unwired (default)Wire the lower red error connector to a fallback arm, or set continue-on-error. See Errors
Happy-path nodes starved after an agent failureYou wired out:err. Success out stayed emptyExpected. Only the err arm receives failure text
Graph lint: wire out:err for a fallback armContinue-on-error without an err wireWire out:err, or turn continue-on-error off
required input not wiredA node that needs an input has noneWire it, or mute it. ⌀ Graph lint lists them all
context not materializedA context node has no payload and no session sourceWire a session / agent, drag from Library, or mute the node
this workflow has N interactive gate(s) … — pass approveAllApproval or live-handoff nodes in a server / detached runAdd --approve-all, use ≫ and confirm, or run in-tab with ▶ for splice
threadle server not reachablestatus / logs / skills / open / run --detach with no serverStart threadle --no-open first
custom node "<name>" is not installedThe graph references a node folder that isn’t on this machineImport or create the node under ~/.config/threadle/nodes/
nothing to run for "<target>"Not a template id, file path, or graph idthreadle --list-templates, or check the path
cycle detected — workflow can never finishThe graph has a loopBreak the cycle. Graph lint flags unreachable nodes
EADDRINUSE at startupPort 4570 already takenthreadle --port 4600
403 forbidden host / cross-origin write rejectedReaching the API from a non-loopback hostnameUse 127.0.0.1, localhost, or [::1]
Features silently not workingBrowser tab and server are different buildsFollow the banner. See below
source: unavailable (no bundled or local prices)Bundle missing and no opencode/legacy cacheSee below
! model usage exhausted … in the run logSubscription pool spent (Claude 5h/7d, Cursor monthly Spending)Pick another model on the agent, wait for reset, or raise Cursor Spending. See Errors

Availability is checked per provider and nothing cascades. A missing provider contributes no sessions and breaks nothing else.

ProviderReported available when
Claude Code~/.claude/projects exists
opencode~/.local/share/opencode/opencode.db exists and can be read
Cursor~/.cursor/projects exists, or agent --version succeeds
Antigravity~/.gemini/antigravity-cli exists, or agy --version succeeds
Codex~/.codex exists, or codex --version succeeds

Storage and CLI are separate signals. Claude Code and opencode report available on storage alone, so you can browse history on a machine where the CLI is gone. A run will then fail at spawn time instead.

If you use CLAUDE_CONFIG_DIR to keep Claude Code’s state somewhere else, threadle needs the same variable in its environment. Launching threadle from a GUI rather than a shell is the usual reason it doesn’t see it. The same applies to ANTIGRAVITY_HOME / JETSKI_APP_DATA_DIR and CODEX_HOME.

The Cursor agent binary is resolved specially, because GUI-launched processes often lack ~/.local/bin on PATH: CURSOR_AGENT_PATH, then ~/.local/bin/agent, ~/.cursor/bin/agent, /usr/local/bin/agent, /opt/homebrew/bin/agent, then PATH.

The Antigravity agy binary is resolved the same way: ANTIGRAVITY_CLI_PATH / AGY_PATH, then ~/.local/bin/agy, ~/.gemini/bin/agy, /usr/local/bin/agy, /opt/homebrew/bin/agy, then PATH.

The Codex codex binary: CODEX_PATH, then ~/.local/bin/codex, ~/.codex/bin/codex, /usr/local/bin/codex, /opt/homebrew/bin/codex, then PATH.


The preflight runs before any agent work, and reports every blocker at once. Muting a node exempts it from all checks. It is the fastest way past a blocker you don’t need for this run.

BlockerWhy it can’t be deferred
Agent with no modelThere is no inspector to ask in
Unwired required inputThe node would starve
Context with neither payload nor session sourceNothing to extract from
Skill or rules node with no fileNothing to inject
Illegal cross-frame wireThe expansion is ambiguous

Gates are the one non-structural case: approval nodes, live handoffs, and circuit breakers set to park all need --approve-all, which auto-forwards the text without an edit step. If you want to splice, run on the canvas.

A context node whose payloadHash points at a deleted blob rematerializes from the wired session when one is available. Otherwise the node fails with a clear error.


Custom nodes are child processes with strict contracts. Each failure mode has its own message:

FailureMessage
Non-zero exitThe process error, with stderr on the ! lane of the run log
TimeoutThe process is killed at the manifest’s timeoutMs. Default 60 000 ms, hard-capped at 300 000 ms. Testing a single node reports timed out after <timeoutMs>ms
Output over 4 MBThe run fails. That is the hard output cap
Input doesn’t match the declared type<node> expects <type> input but not an int: "…"
Named-output node printed non-JSON<node> declares named outputs but did not print a JSON object
Named-output node printed an array or scalar<node> declares named outputs but printed an array
Declared output port absent<node>: output is missing port "<port>"
Required input port unfed<node>: required input "<port>" has no value
Too many at oncetoo many custom nodes running (max 8) — try again in a moment

stderr is captured whether or not the node failed. The first 4 000 characters are written to the run log, and the first 2 000 to the per-job log under runs/logs/. A node that “does nothing” usually printed its real output to stderr.

Value types are text, int, float, bool, json. text accepts anything. int widens to float. Everything else must parse exactly.


threadle detects the classic split. Fresh UI running against an old API process, or the reverse. By comparing its own bundle’s mtime against boot time and reporting both through /api/health. A banner appears at the top of the page:

  • The running server is an older build than the code on disk. Restart it.
  • A newer UI build is available. Hard-refresh this page (⇧⌘R).

The web check allows 30 seconds of slack, so a build finishing mid-session won’t nag you spuriously.


opencode’s SQLite schema moves. At open time threadle checks session for id, title, directory, parent_id, agent, time_updated. If any is missing. Or the file cannot be opened. It logs once and falls back to opencode db --format json for every query:

threadle: opencode.db session table schema unexpected; using `opencode db` CLI fallback
threadle: cannot open opencode.db directly (<err>); using CLI fallback

The direct connection is also retried up to three times on BUSY/LOCKED first. In fallback mode sessions still list and read. It is slower, and it needs the opencode binary on PATH.


Runtime never fetches list prices. Lookup is local only: opencode’s ~/.cache/opencode/models.json, then the bundled snapshot shipped with threadle, then a legacy ~/.config/threadle/models-pricing.json if present.

If every source is missing (broken install with no bundle), Statistics shows source: unavailable (no bundled or local prices). Tracked cost figures are absent. Token counts and actual spend still work. Reinstall / update threadle to restore the bundled table, or ensure opencode has populated its cache.


A run log line starting with ! model usage exhausted means a subscription pool is spent. Not a code bug.

  • Claude. 5h and 7d (weekly) windows (plus Opus 7d when cached) from ~/.claude.json. Agent cards chip at ≥90%. Runs preflight at 100%.
  • Cursor. Monthly Spending pools. threadle only sees the CLI error text (no remaining-% on disk).

Pick another model on the agent-def card, wait for the reset time in the message, or (Cursor) raise limits in Spending. Retry alone won’t help until the window clears.

See Costs · Errors.


  • CLI. threadle check / --providers for env and flag drift
  • CLI. Jobs, inventory (services, agents, …), exit codes, log lanes
  • Files & config. Where run logs are kept
  • Providers. How each adapter discovers and stays fresh
  • Settings. Claude billing mode