Error Catalog
The complete error vocabulary, generated from the err factory map in
@the-40-thieves/obsidian-tc-shared so it never drifts from the running server. See the
API Reference for the response envelope these codes travel in, and the
Tool Catalog for the surface that raises them.
Errors arrive in the standard MCP error shape. The code below is what appears in the structured
payload; it is stable across releases and is what a client should branch on. Messages are defaults
and may be replaced at the throw site with something more specific about the failure.
Every failure obsidian-tc returns carries one of these 36 canonical codes, 36 of which ship a recovery hint. The code is the stable contract — branch on it rather than on the message, which is a human-readable default that may be replaced with something more specific at the throw site.
| Code | Default message | Recovery |
|---|---|---|
aborted |
operation aborted by caller | The caller’s AbortSignal fired before the operation finished. Re-issue the call fresh if the work is still wanted; this is not a transient failure of the operation itself. |
acl_denied |
path denied by folder ACL | This path is outside the folder ACL for this caller. Call inspect_acl to see which roots are permitted and retry within one of them. |
bases_syntax_error |
invalid .base YAML or filter syntax | The .base file’s YAML or filter syntax is invalid. Fix the file — read_base shows how the server parses it. |
command_not_allowlisted |
command is not in the vault allowlist | The command is not in this vault’s allowlist. Add it there (deny-by-default is intentional) — list_commands shows what is currently permitted. |
compute_budget_exceeded |
operation exceeded its compute budget | The input exhausted its compute budget and will do so again identically — do not retry. Simplify the pattern or expression, or narrow what it runs over. |
concurrent_modification |
note changed since it was read | The note changed after you read it. Re-read it, re-apply your edit to the new content, and retry with the fresh CAS token — do not retry the stale one. |
conflict |
conflict | Re-read the current state, re-apply the change on top of it, and retry. |
content_rejected |
content rejected by poison scan | The content tripped the poison scan (risk: high) and was not written. Revise it to remove instruction-override, persistence-directive, hidden-text, or exfiltration-like phrasing, or write it with provenance: “authored” if a human is vouching for it. |
dql_error |
Dataview DQL error | Dataview rejected the query. Check it with validate_dql, which reports the syntax error without running it. |
elicit_invalid |
elicit token invalid or expired | The token was rejected or expired. Re-issue the original call with no token to trigger a fresh confirmation prompt. |
elicit_required |
human confirmation required | A human must approve this call. A client with MCP elicitation gets an inputRequired prompt; otherwise mint one with obsidian-tc elicit --hash <args_hash> --tool <name> and resend. Never reuse an old token. |
embedding_provider_error |
embedding provider failed | The embedding backend failed. Retryable; if it persists, confirm the provider is reachable and the configured model is present (server_health reports the provider). |
execute_command_disabled |
command execution is disabled for this vault | Command execution is off for this vault. It must be enabled in configuration; this is a policy decision, not a transient failure. |
forbidden |
scope or ACL denied | Missing a required scope (details.required lists it). Re-authenticate with a credential that grants it, or call inspect_acl to see what this caller may reach. |
internal |
internal error | Unexpected server-side failure. Retryable once; if it persists, check server_health and the server log rather than retrying further. |
internal_error |
internal error | Unexpected failure inside the handler. Retryable once; if it persists, capture the call and check the server log. |
invalid_input |
invalid input | Check the argument types and required fields against describe_capability’s inputSchema before retrying. |
jsonlogic_error |
JSONLogic expression invalid | The JSONLogic expression is malformed. Verify the operator names and that every operator’s argument arity matches. |
not_found |
not found | Confirm the target exists before retrying — note_exists checks one path, list_notes enumerates, and search_vault finds by content when the exact path is unknown. |
note_exists |
note already exists | Something is already at that path. Choose a different path, or use the tool’s overwrite/append option if it has one. |
note_not_found |
note not found | Confirm the path first: note_exists checks it, list_notes enumerates the folder, search_vault finds the note by content. |
operation_timeout |
operation timed out | The operation exceeded its time budget. Retryable, but narrow it first — a smaller limit or a more selective query is more likely to finish. |
overflow |
response exceeds byte budget | The response exceeded the byte budget. Narrow the request — a smaller limit, fewer paths, or a compact/paginated read — rather than retrying unchanged. |
path_ambiguous |
path resolves to multiple notes | More than one note matches. Pass the full vault-relative path — list_notes or search_vault will show the candidates to choose between. |
path_invalid |
path is invalid | Paths are vault-relative, must stay inside the vault, and must not traverse upward. Rewrite the path rather than retrying it. |
plugin_incompatible |
companion plugin API version is incompatible with this server | The companion plugin’s API major does not match this server’s. Permanent until one side is upgraded; retrying will not help. |
plugin_missing |
required Obsidian plugin not detected | The required Obsidian plugin is not installed or not enabled. Enable it in Obsidian, then run refresh_plugin_capabilities so the server re-probes. |
plugin_unreachable |
plugin detected but REST endpoint failed | The plugin is present but its endpoint failed. Retryable; check Obsidian is running and the Local REST API is enabled and reachable. |
read_only |
server is in read-only mode | The server is in read-only mode, so no write will succeed until that changes. Use a read tool, or take this up with whoever runs the server. |
read_only_mode |
vault is in read-only mode | This vault is configured read-only. Target a writable vault, or change the vault’s configuration; retrying cannot help. |
requires_live_obsidian |
this operation requires a live Obsidian (Local REST API) connection | This capability needs a live Obsidian connection and the vault is headless. Start Obsidian with the Local REST API, or use a filesystem-only equivalent. |
throttled |
rate limit exceeded | Rate limit for this scope class. Back off before retrying, and reduce batch size or spread the calls; a tight retry loop will keep hitting it. |
unauthorized |
authentication required | Authenticate first: send a credential this server accepts, then re-issue the call. |
unsupported_base_filter |
base uses the Obsidian Bases expression DSL, which query_base does not evaluate | This base uses Obsidian’s Bases expression DSL, which query_base does not evaluate. Read the base and filter the rows yourself, or simplify the base’s filter. |
validation_error |
input validation failed | Read the tool’s inputSchema via describe_capability and correct the arguments; details names the offending field where one could be identified. |
vault_not_found |
vault not found | Use one of the ids from list_vaults, or register the vault first with add_vault (no restart needed). |
Reading the Recovery column
Section titled “Reading the Recovery column”Recovery hints say what to do instead; the retryable flag on the error says whether to retry.
They are deliberately separate — a code can be non-retryable and still have a clear next step.
A hint is a fixed string chosen by code alone. No path, query, caller, vault or argument is in
scope where it is selected, so a hint cannot leak vault content by construction rather than by
review. An em dash means the taxonomy declares no hint for that code — a considered choice, made
because guidance that only restates the message is noise, not an omission.
Where a failure can name specifics, those arrive in the error’s details object (for example
details.required on forbidden, or the offending field on validation_error). Read details in
preference to parsing message text.