Match the symptom or exact message you see to its cause and fix.
This page is indexed by what you observe. If you already have an error code, an exception class, or a process exit code, go to Errors and error codes instead — that page is indexed by error identity.
Symptom:
Capture
The SDK validates several options up front, so a typo fails fast instead of silently disabling a feature. These are thrown synchronously, before Qoder CLI is started.
Symptom:
Symptom: The tool is registered, the session starts, and the agent solves the task without it.
Work through these in order:
Symptom: Tool calls are denied, or the run stalls waiting for approval that never arrives.
See Permission Control for the full precedence order.
Symptom:
Cache the decision outside the callback rather than raising the timeout repeatedly — every millisecond here is added to every turn.
Applies to TypeScript only — the
If
Symptom: A settings file enables something, but the session behaves as if it were disabled.
Some query options are authoritative: when provided, they replace the corresponding settings block rather than merging with it, and any switch you omit becomes
Symptom: The agent obeys conventions that are not in your prompt.
Instruction files on the machine running Qoder CLI are loaded by default, and they can include other files. Log what actually loaded:
For reproducible runs across hosts, load nothing from disk with
When you need to escalate, collect these before the state is lost:
Include the SDK version, the Qoder CLI version from the
The session never starts
Symptom: query() yields nothing, or the first await hangs and eventually throws.
| Cause | How to confirm | Fix |
|---|---|---|
The qodercli executable is not on PATH | The error is QoderCliProcessError and mentions the executable | Set pathToQoderCLIExecutable, or export QODERCLI_PATH |
| Startup exceeded the load timeout | Failure occurs after a consistent delay | Raise loadTimeoutMs; a cold start on a slow disk can exceed the default |
| No credential was configured | Error carries code: 'auth_not_configured' | Pass auth; see Authentication |
| The credential env var is unset | code: 'auth_access_token_env_var_not_configured' or ..._service_account_... | Export the variable the auth helper reads |
stderr while diagnosing startup — it usually names the real problem:
An option is rejected before the process starts
The SDK validates several options up front, so a typo fails fast instead of silently disabling a feature. These are thrown synchronously, before Qoder CLI is started.
| Message | Cause | Fix |
|---|---|---|
securityScan contains unknown option: <key>security_scan contains unknown option: <key>. | Misspelled switch name | TypeScript uses l1StaticCheck / l2LightweightScan / l3DeepScan; Python uses l1_static_check / l2_lightweight_scan / l3_deep_scan |
securityScan.<key> must be a booleansecurity_scan.<key> must be a bool. | A string such as 'yes' was passed | Pass true or false |
security_scan must be a dict. | A non-mapping value was passed in Python | Pass a dict of switches |
native mode requires at least one enabled scope; use memory: {} to disable SDK memory | Both projectScope and userScope set to false (TypeScript only) | Keep one scope, or use memory: {} to turn memory off |
[createSdkMcpServer] Server name must be a non-empty string. | Missing or blank name | Give the server a name |
[createSdkMcpServer] Tool name must be a non-empty string in server "<server>". | A tool was defined without a name | Name every tool |
[createSdkMcpServer] Tool "<tool>" must have a non-empty description string. | Missing description | Add a description; the model relies on it to decide when to call the tool |
The SDK and runtime disagree
Symptom: ProtocolVersionMismatchError or UnsupportedCliCapabilityError.
These mean the SDK and Qoder CLI are from incompatible releases, which normally happens when the runtime is managed separately instead of using the one bundled with the package.
UnsupportedCliCapabilityError.capability names the exact missing feature, which tells you whether to upgrade the runtime or stop using that option. Upgrade Qoder CLI, or remove the pathToQoderCLIExecutable override so the bundled runtime is used.
The agent never calls my custom tool
Symptom: The tool is registered, the session starts, and the agent solves the task without it.
Work through these in order:
- Check the fully-qualified name. A tool named
greeton a server namedmy_toolsismcp__my_tools__greetto the model. Allowlists must use the full name. - Check whether an allowlist excludes it. If
toolsorallowedToolsis set and omits the tool, the model never sees it. See Tool naming and allowlists. - Check lazy loading. With MCP lazy loading enabled, tools are not all resident. Mark the ones that must always be available:
- Check the description. The description is the model's only signal for when the tool applies. "Does a thing" gets ignored; state the trigger condition.
The agent stops on a permission it should have
Symptom: Tool calls are denied, or the run stalls waiting for approval that never arrives.
| Cause | Fix |
|---|---|
disallowedTools overlaps the tool | disallowedTools takes priority over allowedTools and permissionMode |
| No approval handler in a non-interactive job | Set a permissionMode that does not require prompts, or supply canUseTool |
Both canUseTool and permissionPromptToolName set | They are mutually exclusive; choose one |
Model selection times out
Symptom: ModelPolicyTimeoutError.
resolveModel runs on the critical path of every request and has a 500 ms default budget. A callback that queries a database or an HTTP service will exceed it.
Memory was not written
Applies to TypeScript only — the memory option does not exist in the Python SDK.
Symptom: The agent learned something, the run finished, and nothing was saved.
Generation runs in a background agent after the turn completes. A process that exits immediately after the final result message can terminate it mid-write.
flushMemory() returns and files are still unchanged, inspect the generation result — no_change and skipped are normal outcomes, not failures. See Give agents persistent memory.
A feature I enabled in settings is off
Symptom: A settings file enables something, but the session behaves as if it were disabled.
Some query options are authoritative: when provided, they replace the corresponding settings block rather than merging with it, and any switch you omit becomes false.
securityScan behaves this way. If settings.json enables l1StaticCheck and the query passes securityScan: { l3DeepScan: true }, then l1StaticCheck is off for that session. Restate every switch you want enabled. See Scan code for security issues.
The agent follows rules I never set
Symptom: The agent obeys conventions that are not in your prompt.
Instruction files on the machine running Qoder CLI are loaded by default, and they can include other files. Log what actually loaded:
settingSources: []. See Customize the system prompt.
Capturing a report worth filing
When you need to escalate, collect these before the state is lost:
system/init message, the session_id, and the Result subtype with any error_code. Redact credentials, prompts, and source content unless the diagnostic explicitly requires them — see Logging and support.
Next steps
- Errors and error codes — look up a specific code, exception class, or exit code
- Authentication — configure credentials and handle expiry
- How it works — understand startup and the agent loop