Troubleshooting
Most failures are refusals, not crashes: threadle preflights a run and stops before spending money. The message names the node.
| Problem | Cause | Fix |
|---|---|---|
| No sessions listed for a provider | Its storage directory does not exist where threadle looks | Install 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 --providers | An installed agent CLI’s help no longer mentions an inject token threadle probes for | Confirm 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 blockers | Fix the named nodes, or mute them |
model not set — pick one in the inspector | An agent node has no model | Pick a model in the inspector. Detached runs cannot ask |
| Agent failed and the whole run stopped | out: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 failure | You wired out:err. Success out stayed empty | Expected. Only the err arm receives failure text |
Graph lint: wire out:err for a fallback arm | Continue-on-error without an err wire | Wire out:err, or turn continue-on-error off |
required input not wired | A node that needs an input has none | Wire it, or mute it. ⌀ Graph lint lists them all |
context not materialized | A context node has no payload and no session source | Wire a session / agent, drag from Library, or mute the node |
this workflow has N interactive gate(s) … — pass approveAll | Approval or live-handoff nodes in a server / detached run | Add --approve-all, use ≫ and confirm, or run in-tab with ▶ for splice |
threadle server not reachable | status / logs / skills / open / run --detach with no server | Start threadle --no-open first |
custom node "<name>" is not installed | The graph references a node folder that isn’t on this machine | Import or create the node under ~/.config/threadle/nodes/ |
nothing to run for "<target>" | Not a template id, file path, or graph id | threadle --list-templates, or check the path |
cycle detected — workflow can never finish | The graph has a loop | Break the cycle. Graph lint flags unreachable nodes |
EADDRINUSE at startup | Port 4570 already taken | threadle --port 4600 |
403 forbidden host / cross-origin write rejected | Reaching the API from a non-loopback hostname | Use 127.0.0.1, localhost, or [::1] |
| Features silently not working | Browser tab and server are different builds | Follow the banner. See below |
source: unavailable (no bundled or local prices) | Bundle missing and no opencode/legacy cache | See below |
! model usage exhausted … in the run log | Subscription pool spent (Claude 5h/7d, Cursor monthly Spending) | Pick another model on the agent, wait for reset, or raise Cursor Spending. See Errors |
No sessions found
Section titled “No sessions found”Availability is checked per provider and nothing cascades. A missing provider contributes no sessions and breaks nothing else.
| Provider | Reported 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.
A detached run refuses to start
Section titled “A detached run refuses to start”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.
| Blocker | Why it can’t be deferred |
|---|---|
| Agent with no model | There is no inspector to ask in |
| Unwired required input | The node would starve |
| Context with neither payload nor session source | Nothing to extract from |
| Skill or rules node with no file | Nothing to inject |
| Illegal cross-frame wire | The 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.
A custom node fails
Section titled “A custom node fails”Custom nodes are child processes with strict contracts. Each failure mode has its own message:
| Failure | Message |
|---|---|
| Non-zero exit | The process error, with stderr on the ! lane of the run log |
| Timeout | The 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 MB | The 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 once | too 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.
Stale build
Section titled “Stale build”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 schema churn
Section titled “opencode schema churn”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 fallbackthreadle: cannot open opencode.db directly (<err>); using CLI fallbackThe 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.
Prices unavailable
Section titled “Prices unavailable”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.
Model usage exhausted
Section titled “Model usage exhausted”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.
Related
Section titled “Related”- CLI.
threadle check/--providersfor 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