nornir error codes — nornir-<subsystem>-<n>
FROZEN. APPEND-ONLY. NEVER REUSED.
The machine half of every non-UI error nornir emits. Each error carries two channels in one message — a visible human string AND this stable code:
[nornir-warehouse-1] cannot open the Iceberg warehouse: /srv/wh: permission denied (os error 13)
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
machine channel human channel (summary) the operand that failed
On gRPC the code ALSO rides as its own x-nornir-code metadata entry, so a thin client can branch on the code without parsing prose.
The law
- FROZEN — a published code never changes meaning. Reword a summary freely; never re-point a code at a different failure.
- APPEND-ONLY — retire a code by leaving it in the registry and ceasing to emit it. NEVER reuse the number: a retired code in an old log must stay unambiguous.
- UNIQUE within its subsystem and globally across the app.
- Consumers (facett panes, korp) may prepend a mount prefix; the parser finds the bracketed code anywhere in the string.
Where it is enforced
The registry is Rust, not prose: src/errcode.rs. Codes are declared through the codes! macro, which enrols each one into errcode::ALL in the same breath — a code cannot enter the app without entering the uniqueness gate.
| gate | what FAILS it |
errcode::tests::codes_are_unique |
a duplicate code, or a duplicate (subsystem, n) — the test that fails on a repeat |
errcode::tests::code_strings_match_their_parts |
a code string that drifted from its (subsystem, n), an empty summary, n == 0 |
errcode::tests::tag_carries_both_channels_and_round_trips |
a message that lost either channel |
errcode::tests::parse_does_not_confuse_prefix_codes |
nornir-rpc-1 recovered from a nornir-rpc-10 message |
server_grpc::errcode_wiring_tests::every_status_helper_carries_both_channels |
a gRPC Status emitted without a code in message AND metadata |
server_grpc::errcode_wiring_tests::the_auth_gate_denies_with_a_typed_code |
an auth denial arriving uncoded |
Run them: cargo test --no-default-features --features server --lib errcode
Emitting a code
use nornir::errcode;
// anyhow / CLI / log — both channels in one string:
anyhow::bail!("{}", errcode::WAREHOUSE_OPEN.tag(format_args!("{}: {e:#}", root.display())));
// gRPC handler — code in the message AND in `x-nornir-code`:
return Err(errcode::status(&errcode::RPC_WORKSPACE_UNKNOWN, tonic::Code::NotFound, name));
Asserting a code in a test
Assert the code, never a substring of the prose (prose is free to be reworded):
assert_eq!(errcode::parse(&msg), Some(&errcode::WAREHOUSE_OPEN));
assert_eq!(errcode::from_status(&status), Some(&errcode::RPC_PERMISSION_DENIED));
Suite namespaces
nornir-<subsystem>-<n> (this app) · holger-<subsystem>-<n> · korp-<subsystem>-<n> · dwarves-<subsystem>-<n> · facet-<component>-<n> (facett UI).
The registry — 55 codes across 12 subsystems
rpc — the nornir.v1 gRPC handler surface (src/server_grpc.rs)
| code | const | means | emitted when |
nornir-rpc-1 |
RPC_UNAUTHENTICATED |
request carried no usable credential | The bearer/mTLS identity on the call is missing or unreadable. |
nornir-rpc-2 |
RPC_PERMISSION_DENIED |
identity is not permitted this resource | Authenticated, but the policy denies this identity the resource. |
nornir-rpc-3 |
RPC_WORKSPACE_UNKNOWN |
no such workspace on this server | The named workspace is not registered on this server. |
nornir-rpc-4 |
RPC_REPO_UNKNOWN |
no such repo in this workspace | The named repo is not a member of the workspace. |
nornir-rpc-5 |
RPC_INVALID_ARGUMENT |
invalid request argument | A request field was absent, empty or malformed. |
nornir-rpc-6 |
RPC_DESCRIPTOR_SHAPE |
workspace descriptor has the wrong shape | The descriptor exists but its shape is wrong (e.g. repo is not a table). |
nornir-rpc-7 |
RPC_INTERNAL |
handler failed internally | The handler failed for an internal reason (the detail carries the cause). |
nornir-rpc-8 |
RPC_UNIMPLEMENTED |
verb not implemented in this build | The verb exists in the proto but this build does not implement it. |
nornir-rpc-9 |
RPC_FAILED_PRECONDITION |
precondition for this call is unmet | A precondition the caller must satisfy first is unmet. |
nornir-rpc-10 |
RPC_UNAVAILABLE |
a backing resource is unavailable | A dependency the handler needs (warehouse, checkout, index) is unavailable. |
Next free ordinal in rpc: 11
warehouse — Iceberg/Arrow/redb store (src/warehouse/)
| code | const | means | emitted when |
nornir-warehouse-1 |
WAREHOUSE_OPEN |
cannot open the Iceberg warehouse | The Iceberg warehouse root could not be opened. |
nornir-warehouse-2 |
WAREHOUSE_APPEND |
warehouse append failed — rows not persisted | An append (one Iceberg snapshot) failed; rows were NOT persisted. |
nornir-warehouse-3 |
WAREHOUSE_READ |
warehouse read failed | A read/scan over a warehouse table failed. |
nornir-warehouse-4 |
WAREHOUSE_TABLE_MISSING |
warehouse table does not exist | The table does not exist in the catalog (never populated). |
nornir-warehouse-5 |
WAREHOUSE_SCHEMA_DRIFT |
Arrow batch does not match the table schema | An Arrow batch did not match its declared schema (column count/type drift). |
nornir-warehouse-6 |
WAREHOUSE_REDB |
redb sidecar store failed | The redb sidecar (cache/index) could not be opened or written. |
nornir-warehouse-7 |
WAREHOUSE_QUERY |
warehouse query was rejected | SQL submitted to the warehouse query engine was rejected. |
nornir-warehouse-8 |
WAREHOUSE_LOCKED |
warehouse writer lock is held | The warehouse writer lock could not be taken (another writer holds it). |
Next free ordinal in warehouse: 9
knowledge — syn symbol/call-edge/git-heat scans (src/knowledge/)
| code | const | means | emitted when |
nornir-knowledge-1 |
KNOWLEDGE_SCAN |
knowledge scan failed | The repo could not be scanned (path wrong, unreadable, not a checkout). |
nornir-knowledge-2 |
KNOWLEDGE_EMPTY_SCAN |
knowledge scan resolved to nothing — check the repo path | The scan produced ZERO files/symbols — almost always a wrong repo path. |
nornir-knowledge-3 |
KNOWLEDGE_WATCH |
knowledge file-watch failed | The file watcher backing incremental scans failed. |
Next free ordinal in knowledge: 4
dwarf — binary/DWARF introspection (src/introspect/)
| code | const | means | emitted when |
nornir-dwarf-1 |
DWARF_READ_BINARY |
cannot read the binary | The binary could not be read as an object file. |
nornir-dwarf-2 |
DWARF_NO_DEBUG_INFO |
binary has no debug info | The binary carries no debug info (stripped) — nothing to extract. |
nornir-dwarf-3 |
DWARF_PERSIST |
cannot persist DWARF facts | Persisting the extracted DWARF facts into the warehouse failed. |
Next free ordinal in dwarf: 4
vector — embeddings + the search index (src/vector/, src/index/)
| code | const | means | emitted when |
nornir-vector-1 |
VECTOR_MODEL_LOAD |
cannot load the embedding model | The embedding model could not be loaded. |
nornir-vector-2 |
VECTOR_EMBED |
embedding failed | Embedding a batch failed. |
nornir-vector-3 |
VECTOR_STORE |
vector store failed | The vector store could not be opened or written. |
nornir-vector-4 |
VECTOR_PROFILE_MISMATCH |
vectors come from different model profiles — not comparable | A similarity query mixed incomparable model profiles (dim/model mismatch). |
nornir-index-1 |
INDEX_STORE |
text index failed | The text index (tantivy) could not be opened, written or queried. |
Next free ordinal in vector: 5
docs — the doc generator / renderer (src/docs/)
| code | const | means | emitted when |
nornir-docs-1 |
DOCS_SOURCE_MISSING |
doc source is missing | A .nornir doc source is missing. |
nornir-docs-2 |
DOCS_RENDER |
doc render failed | Rendering (typst/markdown → PDF/HTML/SVG) failed. |
nornir-docs-3 |
DOCS_EXPORT |
doc export failed | Export/publish of a rendered artefact failed. |
nornir-docs-4 |
DOCS_UNRESOLVED_PATH_DEP |
doc render needs unresolved sibling path-deps | A render needs sibling path-deps that are not resolvable. |
nornir-docs-5 |
DOCS_STALE |
rendered docs are behind their sources | The docs freshness check found the rendered book behind its sources. |
Next free ordinal in docs: 6
guard — the forbidden-path guard (crates/nornir-guard)
| code | const | means | emitted when |
nornir-guard-1 |
GUARD_WRITABLE |
a guarded path is writable | A path the guard declares forbidden is writable. |
nornir-guard-2 |
GUARD_MANIFEST |
guard manifest is unreadable | The guard manifest is missing or unreadable. |
nornir-guard-3 |
GUARD_DRIFT |
a guarded path drifted from the manifest | A guarded path drifted from the recorded manifest. |
Next free ordinal in guard: 4
release — the release pipeline + gates (src/release/)
| code | const | means | emitted when |
nornir-release-1 |
RELEASE_DIRTY_TREE |
release refused — uncommitted changes | The release refuses to run because a repo tree is dirty. |
nornir-release-2 |
RELEASE_GATE_REFUSED |
a release gate refused | A release gate refused (test/bench/guard/coverage/docs). |
nornir-release-3 |
RELEASE_LINEAGE |
cannot persist release lineage | Writing the release_lineage rows failed. |
nornir-release-4 |
RELEASE_REGRESSION |
regression against the prior release | A regression was detected against the prior released artefact. |
nornir-release-5 |
RELEASE_PUBLISH |
publish failed | Publishing to the registry failed. |
Next free ordinal in release: 6
funnel — the idea→plan→node DAG store (src/funnel/)
| code | const | means | emitted when |
nornir-funnel-1 |
FUNNEL_STORE |
funnel store failed | The funnel store could not be opened or written. |
nornir-funnel-2 |
FUNNEL_NODE_UNKNOWN |
no such funnel node | The referenced funnel node/plan does not exist. |
nornir-funnel-3 |
FUNNEL_CYCLE |
link would create a cycle | The requested link would create a cycle in the DAG. |
Next free ordinal in funnel: 4
security — SBOM / vulnerability / licence scanning (src/security/)
| code | const | means | emitted when |
nornir-security-1 |
SECURITY_SBOM |
cannot produce the SBOM | The SBOM for a repo could not be produced. |
nornir-security-2 |
SECURITY_VULN_FEED |
vulnerability feed unavailable | The vulnerability feed could not be consulted. |
nornir-security-3 |
SECURITY_REACHABLE_VULN |
a reachable vulnerability was found | The scan found a REACHABLE vulnerability (a refusal, not a transport error). |
Next free ordinal in security: 4
mcp — the MCP verb surface (src/bin/nornir-mcp.rs)
| code | const | means | emitted when |
nornir-mcp-1 |
MCP_INVALID_ARGS |
invalid MCP verb arguments | An MCP verb argument was absent or malformed. |
nornir-mcp-2 |
MCP_VERB_FAILED |
MCP verb failed | The MCP verb failed while doing its work. |
nornir-mcp-3 |
MCP_PATH_MISSING |
path argument does not exist | A path argument handed to an MCP verb does not exist. |
Next free ordinal in mcp: 4
workspace — descriptor, members, populate/monitor (src/workspace/)
| code | const | means | emitted when |
nornir-workspace-1 |
WORKSPACE_DESCRIPTOR |
cannot read the workspace descriptor | The workspace descriptor could not be read or parsed. |
nornir-workspace-2 |
WORKSPACE_MEMBER_PATH |
member source path does not resolve | A member's source path does not resolve. |
nornir-workspace-3 |
WORKSPACE_NO_CHECKOUTS |
populate materialised no checkouts | Populate produced no materialised checkouts. |
nornir-workspace-4 |
WORKSPACE_FETCH |
member clone/fetch failed | A git clone/fetch for a member failed. |
Next free ordinal in workspace: 5