Troubleshooting
This page focuses on common user-facing issues around installation, config, auth, sessions, extensions, and performance.
Startup failures
Section titled “Startup failures”Check first:
- Whether your Go version meets the requirement
- Whether you are using the correct source entry point:
go run ./cmd/chord/ - Whether
config.yamlis missing or malformed - Whether
auth.yamlhas obvious YAML formatting errors
If config.yaml is missing, run chord once in an interactive terminal to launch the setup wizard. If stdin is redirected but Chord can still open the controlling TTY, the wizard runs there. If there is no controlling TTY, Chord exits immediately with an initialization error. If config.yaml exists but is malformed, fix the file first; the wizard only runs for missing configs.
If you are using a built binary, rerun it and inspect terminal error output.
401 / 403 / auth failures
Section titled “401 / 403 / auth failures”Check:
- Whether provider names in
auth.yamlmatchconfig.yaml - Whether the API key is valid
- Whether OAuth providers have
preset: codex
You can run:
chord doctor modelsTo narrow the check to a known model or model pool:
chord doctor models --model openai/gpt-5.5@highchord doctor models --pool thinkingLarge OAuth account pools start slowly
Section titled “Large OAuth account pools start slowly”When auth.yaml contains hundreds or thousands of OpenAI / ChatGPT OAuth accounts, Chord loads credential metadata in the background. Missing metadata alone should not block startup.
- Personal Plus/Pro accounts may carry only
user_idand nochatgpt_account_id. They can still be used for ordinary requests, but Chord omitsChatGPT-Account-IDand skips account-id-dependent Codex usage / rate-limit polling for them. account_user_id mismatchoraccount_id mismatchlogs mean metadata explicitly configured inauth.yamlconflicts with what the token itself exposes. Fix or remove that credential.
When manually converting Codex/sub2api exports, keep any available email, account_id, and account_user_id. Use chord doctor models for deliberate account diagnostics.
429 / quota exhausted
Section titled “429 / quota exhausted”Common causes:
- the key has reached its quota
- provider rate limiting
- concurrency or high-frequency requests triggering rate limits
Recommendations:
- switch to another key
- reduce concurrency or retries
- check for accidental looped calls
To diagnose which keys or models are hitting rate limits or errors:
- Press
Ctrl+Ein the TUI to open the error panel, which shows all retry errors including 429s with the provider, model, and maskedkey=...label. - Check the error panel to see the pattern: if one specific key is repeatedly hitting 429, that key is rate-limited; if multiple keys on the same provider fail with 503, the provider itself may be degraded.
UI note:
- the right-side RATE LIMIT panel shows the last Codex rate-limit snapshot (e.g.
5h: 42% 2h30m). When a reset timestamp is reached, the countdown may disappear briefly while Chord refreshes usage; depending on provider semantics (rolling windows), usage may drop gradually rather than jumping straight to 0%. - Codex OAuth runtime state is also reloaded from
auth.state.jsonwhen another Chord process updates that file, so quota snapshots, reset timers, account metadata, and account status changes should appear without restarting the current session. - if the RATE LIMIT panel looks stale, enable debug logs with
log_level: debugand checkchord.logfor lines likeresponses codex ws: rate_limits event ...(received) orresponses codex ws: rate_limits event ignored ...(unrecognized / parse failure).
TUI started, but requests fail
Section titled “TUI started, but requests fail”Check:
- whether the current provider / model exists
- whether the network can reach the API
- whether proxy configuration is effective
For example:
curl -I https://api.anthropic.comcurl -I https://api.openai.com/v1OpenAI-compatible 400s and timeouts
Section titled “OpenAI-compatible 400s and timeouts”Set official_api: true for endpoints that follow official API error semantics. Chord then treats HTTP 400 as a terminal request error. For aggregating or proxy gateways that may wrap upstream failures as HTTP 400, set official_api: false or omit the field so unknown 400s can use the normal retry and fallback path. preset: codex providers are treated as official automatically.
If requests remain in connecting and then retry, test the endpoint directly, check proxy settings, and inspect the error panel. Chord applies a connection timeout so one unavailable key or gateway does not wait indefinitely.
DeepSeek / OpenAI-compatible thinking-mode 400s
Section titled “DeepSeek / OpenAI-compatible thinking-mode 400s”If you use a chat-completions provider such as DeepSeek and see errors like:
The reasoning_content in the thinking mode must be passed back to the API.Invalid assistant message: content or tool_calls must be set
this usually means the provider requires thinking/reasoning content from the previous tool round to be included again in the follow-up request, with a strict assistant message shape. If the same error keeps repeating, keep the corresponding session dump / trace for diagnosis.
Enable compat.reasoning_continuity.mode: openai_visible on the affected model
or provider. This option replays assistant reasoning_content and lets Chord
map portable visible reasoning from other wire families into
reasoning_content; add any provider-specific thinking flags through
compat.request_overrides.body.
For GLM Preserved Thinking, that body override must include
thinking.type: enabled and thinking.clear_thinking: false. For DeepSeek it
only needs thinking.type: enabled. In both cases, replayed
reasoning_content must remain complete, unchanged, and in order.
Codex WebSocket 400 “No tool call found for function call output”
Section titled “Codex WebSocket 400 “No tool call found for function call output””Chord normally recovers from this WebSocket conversation-state mismatch automatically by retrying with the full local conversation. If the error repeats, export a diagnostics bundle and include the session ID in your report.
MCP never becomes ready
Section titled “MCP never becomes ready”Check first:
- whether the MCP URL is reachable
- whether the config name is correct
- whether local mode is simply still initializing MCP asynchronously
Note: a brief gray pending state right after startup does not necessarily indicate an error.
LSP / MCP rows turn gray after the session is idle
Section titled “LSP / MCP rows turn gray after the session is idle”If the right-side environment panel shows LSP or MCP rows in gray after the agent has been idle for a while, that does not necessarily mean the integration failed.
Chord can unload idle LSP and MCP runtime resources to reduce background cost. In that state:
- the row is shown in a dim gray idle state instead of an error color;
- this means the resource was intentionally unloaded while the session was idle;
- on the next real request / busy cycle, Chord restores the runtime dependency before rebuilding the request surface.
Treat it as a problem only when the row stays red, keeps showing a real connection/configuration error, or the next request fails to restore it.
No diagnostics after writing files
Section titled “No diagnostics after writing files”If you configured LSP but do not see diagnostics after writing files:
- check whether the corresponding language server is installed locally
- check whether the
lspconfig format is correct - confirm the target file type matches
file_types - check whether
diagnostics.enabled: falseturned off post-tool diagnostics
For Python specifically:
- Small files use
diagnostics.python.semantic_backend(usuallylsp.pyright). Make surediagnostics.python.semantic_backend.servermatches the server key underlsp. - Large Python files use Ruff quick diagnostics when
ruffis onPATH. - If a large Python file reports diagnostics skipped because Ruff is unavailable, install Ruff or set
diagnostics.python.large_file.run_semantic_when_quick_unavailable: trueto force Pyright on large files too. - Ruff quick diagnostics do not update the LSP sidebar; they appear only in
edit,apply_patch, orwritetool results and clearly note that full Python semantic diagnostics were skipped.
Recommended Python skeleton:
lsp: pyright: command: pyright-langserver args: ["--stdio"] file_types: [".py", ".pyi"]
diagnostics: python: quick_backend: type: command command: ruffFor the full recommended config, see Configuration — Post-tool diagnostics.
Session resume issues
Section titled “Session resume issues”If --continue or --resume does not appear to work as expected:
- confirm the current directory belongs to the same project as the original session
- try explicitly using
--resume <session-id> - check whether restore is only slow rather than actually lost
Chord automatically repairs incomplete turns caused by an interrupted process before resuming. If restored model/provider state or conversation order looks wrong, export a diagnostics bundle from a current build and include the session ID.
A delegated SubAgent appears stuck or an escalate card keeps running
Section titled “A delegated SubAgent appears stuck or an escalate card keeps running”Current builds automatically recover two failure modes that older sessions could expose:
If MODEL or Pool is empty after resuming and focusing a SubAgent, the current build first uses model refs stored in the task, recovery snapshot, and usage ledger. For legacy records without model data, it resolves the latest Agent configuration, so task or meta files do not need manual editing.
On restore, the durable task_id is the stable identity of delegated work. Agent IDs such as explorer-4 and explorer-6 are successive runtime instances of that task. Historical instance transcripts are merged by task, while the sidebar and focus router expose only the task’s canonical latest instance. Do not treat an older agent_id as a separate task or copy/delete instance files to alter restore behavior.
If the TUI becomes unresponsive after switching to a large SubAgent transcript or pressing Enter to continue from a parked SubAgent, the current build uses a bounded transcript window and moves transcript loading, rehydration, and continuation off the TUI update path. A separate rehydration failure also existed: the info panel could recurse through MainAgent.InvokedSkills → SubAgent.InvokedSkills → MainAgent.InvokedSkills until stack overflow. That routing cycle is removed. Agents share a workspace skill catalog, but MainAgent and each SubAgent apply their own latest permissions and keep invoked state separate.
Process stderr now writes directly to the rotating log file instead of being consumed from a pipe by a Go goroutine and fed back into the logging system. A runtime fatal can therefore write its complete stack and exit instead of appearing as a permanent freeze where every key, including raw-mode Ctrl+C, stops working. If an old build is still stuck, terminate it from another terminal and run reset in the original terminal before exporting diagnostics.
- When a parked SubAgent is rehydrated, queued input explicitly wakes its event loop. A startup watchdog retries that wake once if the worker stays
runningwithout creating a turn. - If the worker still cannot start, or its provider/model retries end in a terminal error, Chord marks the task failed, records a
risk_alert, and wakes the owner/MainAgent to retry, reassign, or report the blocker. It does not fabricate a successfulcompleteresult.
An escalate request is a local coordination event, not a long-running network operation. Older runtimes could persist its tool result immediately before the assistant tool call, making a completed card look pending after resume. Current builds repair this narrowly when the adjacent messages have the same tool_call_id; unmatched orphan results are still discarded.
If a session created by an older build is already stuck:
- rebuild or install the current Chord version and restart with
--resume <session-id>; - use the stable
task_id, not the previous runtimeagent_id, when retrying or targeting the delegated task; - do not manually edit
agents/*.jsonl,subagents/tasks.json, or mailbox files; - with
log_level: debug, inspectchord.logforstartup watchdog retrying wake,SubAgent failed, orremoved orphan tool messages.
The normal post-response watchdog may ask a live worker to call complete or escalate. A worker that cannot recover is instead closed as failed and routed back to its owner; failure is never treated as successful completion.
TUI cards show strange colors or broken layout when viewing logs / dumps / shell output
Section titled “TUI cards show strange colors or broken layout when viewing logs / dumps / shell output”If a tool card, local shell result, question dialog, or confirmation summary shows unexpected colors, background leaks, or broken wrapping while viewing diagnostic dumps or raw command output:
- retry the same
read,shell,web_fetch, or local shell action - if you still see corruption, save the original file/output and a screenshot together
Chord displays external tool output as terminal-safe plain text. If the same content consistently breaks layout, attach the original text and a screenshot so the rendering case can be reproduced.
Output-triggered TUI render panic / process killed
Section titled “Output-triggered TUI render panic / process killed”If the outer launcher only shows:
Error: program was killed: program experienced a panicand the session must be resumed with --resume, first inspect the end of ~/.local/state/chord/logs/chord.log for the Go panic stack. main.jsonl usually does not contain this panic text because the failure happened in the TUI render path, not as a persisted conversation message.
If the stack contains these frames, treat it as a TUI markdown/ANSI rendering issue first instead of blaming the model or the last tool result directly:
github.com/charmbracelet/x/ansi.(*Parser).Advancecharm.land/lipgloss/v2.(*WrapWriter).Writecharm.land/glamour/v2/ansi.(*HeadingElement).Finishgithub.com/keakon/chord/internal/tui.renderMarkdownContentWhen checking dumps, note:
- If the last shell/tool result before the crash was already written to
main.jsonl, that tool output was usually not lost. - If Chord was waiting on an LLM SSE stream at the time of the crash, the corresponding
dumps/llm/*.jsonmay contain onlyrequest_body, partialsse_chunks, andreading SSE stream: context canceled. That means the LLM response was dumped only up to the interruption, with no complete final text. context canceledis usually a consequence of process shutdown, not necessarily the root cause.
Attach the panic stack, diagnostics bundle, terminal name/version, and the content being rendered to the issue report.
Screen corruption after switching tabs or refocusing the terminal
Section titled “Screen corruption after switching tabs or refocusing the terminal”If the TUI occasionally shows stale rows, horizontal line artifacts, or partially broken tool cards right after switching tabs or returning focus to the terminal window:
- if the screen is already corrupted, lightly resizing the terminal or switching away and back again can force a full redraw
- if it still reproduces, capture a diagnostics bundle and a screenshot together
If you see corruption right after focus restore while the UI is streaming output, capture a diagnostics bundle plus a screenshot so maintainers can compare Chord’s rendered frame with the terminal’s visible output.
Note: fragments like ;250m pyright during a corruption episode are usually not LSP text but the tail of a truncated ANSI/OSC control sequence.
Repeated separator lines / stale border artifacts
Section titled “Repeated separator lines / stale border artifacts”If the main symptom is repeated horizontal lines, duplicated input/status separators, stale card borders, or old sidebar borders:
- Take a screenshot before forcing a redraw. Include the full terminal window, especially the input area, status bar, and right sidebar.
- Export a diagnostics bundle (
Ctrl+G) immediately, before resizing the terminal — the bundle captures the frame Chord most recently rendered, so it lets maintainers tell a Chord-drawn duplicate from a stale terminal artifact. - Attach both to your report, plus your terminal emulator name and version.
Two quick local observations also help narrow it down:
- If the extra line disappears when the terminal is made one or two columns narrower, mention that — it points at right-edge wrap behavior.
- If the artifact appears right after image preview, paste image, or diagnostics export, mention that too.
Bottom transcript rows are unreachable in long sessions
Section titled “Bottom transcript rows are unreachable in long sessions”If the last transcript rows appear clipped, the final card seems to touch the input separator, or scrolling to the bottom still leaves part of the latest conversation hidden:
- pay special attention to whether the issue starts after long-running background jobs or durable status updates in a long session
- if it still reproduces, capture a screenshot and logs so the transcript state can be compared with the rendered bottom rows
A file-edit tool warns that the file changed since it was observed
Section titled “A file-edit tool warns that the file changed since it was observed”This warning means the file changed after the agent last read it. Chord validates edits against the current contents; write and delete may also create a backup before continuing.
Common causes:
- the file was modified by another process (editor/formatter) between
readandedit/apply_patch; - another agent or Chord process changed the file;
- the file changed during a formatter, generator, or build step.
Re-run read before retrying. If Chord creates a backup, the tool result includes its path under the current session directory. See Edit tools for edit and apply_patch matching behavior.
apply_patch reports hunk not found
Section titled “apply_patch reports hunk not found”apply_patch matches hunks line-by-line and applies the first match after the current search position. It can tolerate common whitespace and Unicode punctuation differences, but repeated blocks still need enough nearby context to make the intended location clear.
If you see this:
- re-run
readon the file and rebuild the patch from the latest content; - re-copy the target block from the latest
readoutput and make sure context/removal lines match the current indentation; if the hunk came from old numbered output, remove any copied line-number prefix first; - when the same block repeats in the file, add nearby unchanged lines to the
@@hunk, use an@@ headeranchor, or pin a tail edit with*** End of File, so the intended occurrence is unambiguous; - split a broad patch into smaller envelopes or smaller hunks;
- do not run external
apply_patchthroughshell; use Chord’s nativeapply_patchtool so permissions, stale tracking, diffs, LSP, and rollback stay connected.
Performance issues
Section titled “Performance issues”If scrolling, streaming output, or large message rendering feels noticeably slow:
- reduce the current session context size (
/compact, or a new session for unrelated work) - compare behavior in different terminals
See Performance for how rendering and streaming are optimized and how to capture a CPU profile for a bug report.
Compaction not triggering / triggering too often
Section titled “Compaction not triggering / triggering too often”Symptom: context usage is high but compaction never runs; or the opposite — frequent compaction disrupts your workflow.
What to check:
- Verify
context.compaction.thresholdis set and greater than 0 (0 disables automatic compaction). - Check the
Contextpercentage in the TUI footer or info panel. It is based on the usable input budget, not the total context window, so it may be lower than expected (see Context management — Compaction). - If
context.compaction.reservedis set, compaction triggers at a lower absolute token count because the reserve is subtracted before applyingthreshold; if compaction is too frequent, check whether reserved is too large. /compact --notemporarily disables automatic compaction for the current session. Restart the session or run/compactto re-enable.- If your gateway returns missing or zero usage, enable
log_level: debugand look forestimated_input_tokensandeffective_input_tokensin automatic-compaction logs.
Note: loop mode does not disable automatic compaction. It only disables request-level context reduction for newly added messages.
Reduction trimming important content
Section titled “Reduction trimming important content”Symptom: the model seems to “forget” earlier tool output, but the session file on disk still contains it.
What to check:
- This is normal behavior for context reduction: stale tool output is trimmed from each LLM request prompt, but never modifies session files on disk.
- If you frequently need to revisit earlier read/search results, raise
read_like_age_turnsandread_like_output_bytes. - If build/test logs remain important context, raise
shell_success_bytes. - For more conservative trimming behavior, raise all
*_age_turnsand*_bytesvalues.
See Context management — Reduction.
Requests rejected: “context length” / “input too large”
Section titled “Requests rejected: “context length” / “input too large””Symptom: the provider returns an error like “context length exceeded” or “input too large”.
What to check:
- Verify that
limit.inputandlimit.contextare correctly configured for your model. If the provider publishes a separate input cap, you must also configurelimit.input. - Check if
context.compaction.thresholdis too high, causing automatic compaction to fire too late. - Increase
context.compaction.reservedto trigger compaction earlier, avoiding rejected requests. - If this happens frequently, use
/compactto manually compact immediately, or lowerthreshold. - With
log_level: debug, search the logs foroversizeto confirm whether oversize recovery (compact then retry) was triggered. If automatic compaction is disabled, Chord stops and reports that all attempted candidate models exceeded the current context instead of retrying indefinitely.
When to check logs
Section titled “When to check logs”Check logs first when you encounter:
- provider request failures where the terminal only shows a summarized error
- context compaction not triggering / context limit issues
- MCP / LSP initialization errors
- hook execution results that differ from expectations
- incomplete headless integration events
Default logs directory: ${XDG_STATE_HOME:-~/.local/state}/chord/logs/. The current log file is chord.log; rotated files are chord.log.1 and chord.log.2.
Override with --logs-dir <path> or CHORD_LOGS_DIR=<path>. To reproduce and collect logs quickly:
chord --logs-dir ./chord-logs