Error reference¶
Every TOVIO error carries a stable TVO-<AREA>-NNN code, a three-part message, and an exit-code class.
This page is the lookup table: the three-part standard, the exit-code classes, the structured --json
shape, and the catalog of the codes you are most likely to hit.
Errors are product surface, not exhaust. The first principle is that the best error is one you never show: TOVIO's architecture removes whole classes of error. A diverged lane reconciles on the lane into a merge-or-conflict commit, so "diverged history" is not an error; a stored conflict means a merge succeeds rather than failing.
The three-part standard¶
Every TOVIO error has three parts, always in this order:
- What happened — a single, plain-language statement of the failed action. Names the concrete object or path. No jargon, no error code in the first line.
- Why — the cause, stated as fact you can verify: which policy, which attribute, which token, which hash. A permission error states what you have alongside what was required, so the gap is self-evident.
- What to do next — one or more concrete steps, each with a runnable command where one exists. The most likely remediation comes first.
A permission, key, or crypto error never renders as a bare "denied". It names the missing attribute,
capability, or key, and prints a diagnostic command (tovio access check, tovio key status,
tovio agent show).
Severity glyphs¶
The default (TTY) rendering uses a leading glyph to classify severity. The glyph is decorative — the
classification is also carried in the exit code and the --json code.
| Glyph | Meaning |
|---|---|
✗ |
Error — the operation did not complete. |
! |
Failure with a standing consequence (e.g. an expired key; some files now unreadable). |
⚠ |
Warning / proactive notice (e.g. key expires in 3 days). Not a failure. |
✓ |
Success that nonetheless needs follow-up (e.g. a merge completed with conflicts). |
Exit-code classes¶
Exit codes are coarse — one class per area — so scripts branch on category while the precise code
lives in --json.code.
| Exit | Class | Areas |
|---|---|---|
0 |
Success (may carry ⚠/✓ follow-ups; conflict creation is success) |
— |
1 |
Generic / uncategorized failure | fallback, plus every refusing CONFLICT code |
2 |
CLI usage error (bad flags, unknown command, missing confirmation) | CLI |
7 |
Storage / integrity (corruption, obliteration, I/O) | STORE |
11 |
Crypto / key material | KEY, CRYPTO |
13 |
Permission / policy / capability denial | PERM, TOKEN, LOCK |
17 |
Sync / wire protocol | SYNC |
19 |
Change / op-log (undo / redo / history) | OP |
21 |
Migration / bridge | MIG |
Conflicts and divergence are not errors
Storing a conflict exits 0 — the merge completed. There is deliberately no error code for "the
remote has work you don't have", "diverged history", or "push rejected"; a divergent sync reconciles the
lane into a two-parent merge-or-conflict commit and is reported as success. (The one guarded case is
sync --push without a pull — a divergent push is refused with TVO-SYNC-006 rather than clobbering the
remote tip; run a full tovio sync to reconcile first.)
The --json shape¶
--json is the agent- and script-facing contract. It is stable across versions: field names and the
code string are part of the compatibility surface. Success uses a top-level result; failure uses
error. The two share one schema.
{
"error": {
"code": "TVO-PERM-001",
"area": "perm",
"category": "authorization",
"exit_class": "permission",
"retryability": "after-user-action",
"title": "Cannot read config/production/api-keys.env",
"cause": "Read requires role=senior AND clearance=secrets; you have role=staff, team=backend; missing clearance=secrets.",
"remediation": [
{ "text": "Diagnose access", "command": "tovio access check config/production/api-keys.env" },
{ "text": "Request the missing attribute", "command": "tovio access request config/production/** --attr clearance=secrets" }
],
"context": {
"path": "config/production/api-keys.env",
"policy_requires": "role=senior & clearance=secrets",
"identity": "did:tovio:dustin",
"your_attributes": ["role=staff", "team=backend"],
"missing": ["clearance=secrets"]
},
"exit_code": 13,
"docs": "https://tovio.dev/errors/TVO-PERM-001"
}
}
| Field | Type | Meaning |
|---|---|---|
code |
string | The stable error code. The primary key for programmatic handling. |
area |
string | Closed subsystem identifier derived from code — e.g. perm, sync. |
category |
string | Closed semantic category for presentation and policy — e.g. authorization. |
exit_class |
string | Closed symbolic form of exit_code — e.g. permission. |
retryability |
string | never, after-user-action, or a catalogued transient. Do not infer it from the area or the numeric exit. |
title |
string | The What — one line, names the object / path. |
cause |
string | The Why — required-vs-actual stated as fact. |
remediation |
array | The What-to-do-next, ordered most-likely-first. Each item has text and an optional command. |
context |
object | Code-specific structured facts. Never includes a value the caller is not authorized to read. |
exit_code |
int | The process exit code, echoed for non-process consumers (MCP). |
docs |
string | Optional deep link to the per-code documentation. Absent for the generic TVO-CORE-000 fallback, which has no per-code page. |
code, area, category, exit_class, retryability, title, cause, remediation, and context
are the required set. An older envelope can omit the four typed fields (area, category, exit_class,
retryability), which a reader derives from the (code, exit_code) pair; docs is additive and appears
only when the code has a per-code page.
Only title, cause, and remediation[].text are localizable. The code, the context keys, the
command strings, and the exit code are the locale-invariant machine surface.
Code namespace¶
A code has the form TVO-<AREA>-<NNN>, where <AREA> is drawn from a closed set and <NNN> is a
zero-padded, stable, three-digit number. Numbers are never reused; a retired code is tombstoned.
The areas below are the ones this page catalogues. The closed set is larger: it also covers the generic
engine fallback (CORE), identity and device control (IDENT), provenance (PROV), the semantic layer
(SEM), tags (TAG), plugins (PLUGIN), CI (CI), the Forge and its hosted API (FORGE, CAPI,
GHC, MFA, NET, ADMIN), issues and releases (COLLAB, REL), and more. A code from any of them
is well-formed; it is simply not one of the everyday failures listed here.
| Area | Covers | Exit |
|---|---|---|
STORE |
Object store: corruption, hash mismatch, obliteration, I/O, GC | 7 |
PERM |
Policy / attribute read denial, write-policy / ABS rejection | 13 |
KEY |
Key material: missing, expired, unwrap failure, rotation | 11 |
CRYPTO |
Low-level crypto: bad signature, AEAD / tag failure, malformed envelope | 11 |
SYNC |
Sync / wire: transport, peer protocol, partial transfer | 17 |
CONFLICT |
Conflict objects (creation is not an error; landing onto a protected lane is) | 0 / 1 |
TOKEN |
Capability tokens: validity, scope, expiry, revocation, delegation | 13 |
LOCK |
File & asset locks: already-held path, land / rewrite refused on an exclusive-lockable path | 13 |
OP |
Change / op-log: undo / redo / history, change resolution | 19 |
CLI |
CLI usage: bad flags, unknown command / path, confirmation gates | 2 |
MIG |
Migration / Git bridge interop | 21 |
Catalog of important codes¶
The messages below are illustrative; the three-part structure, the named cause, and the recovery command
are normative. Codes whose result is a success carry a top-level result (not error) in --json and
exit 0.
Permissions — PERM¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-PERM-001 (PermissionDenied) |
13 | A reader's attributes do not satisfy a policy object's read_policy. Names the missing attribute. |
tovio access check <path> |
TVO-PERM-002 (WritePolicyUnsatisfied) |
13 | A push modifies a write-protected path without a satisfying ABS proof. Only the protected path is blocked. | tovio access check --write <path> |
Keys & crypto — KEY, CRYPTO¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-KEY-001 |
11 | The caller's attribute certificate expired; gated files are now unreadable. Surfaces proactively in tovio status. |
tovio key status, then ask the Key Authority to re-issue the claim |
TVO-KEY-002 |
11 | No local key material for an identity (new device, cleared keychain). | Unlock the OS keychain, or tovio key import <backup> --passphrase-file <file> |
TVO-KEY-003 |
11 | tovio key import is given a backup for a different identity. |
Import the matching backup, or a fresh repo. |
TVO-KEY-005 |
11 | The identity-rotation journal failed verification (fails closed, trusts no old key). | Restore .tovio/ from a clean copy, or key import. |
TVO-CRYPTO-001 |
11 | A policy object's ciphertext fails AEAD authentication (corrupt or wrong recipient key). Not a permission problem. | tovio fsck |
TVO-CRYPTO-002 |
11 | An object's Ed25519 signature does not verify against the claimed author. The object is rejected. | tovio fsck |
Conflicts — CONFLICT¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-CONFLICT-001 |
0 | A merge could not auto-merge a path. This is success — conflicts are stored; nothing is blocked. | tovio conflicts |
TVO-CONFLICT-002 |
1 | A resolve cannot apply (already resolved, unparseable, or out of scope). |
tovio conflicts |
TVO-CONFLICT-003 |
1 | land (or Forge propose / push) would advance a protected lane to a change with unresolved conflicts. |
tovio resolve, then re-land. |
TVO-CONFLICT-004 |
1 | sync / fetch would materialize a conflicted teammate change into your tree as a dependency. The objects still download; only the materialization is refused. |
tovio change notify <id>, or tovio materialize --isolate to build against the last-known-clean version |
TVO-CONFLICT-005 |
0 | Advisory: a change alters an interface dependent changes still reference. Blocks only if a protected lane requires semantic-check. |
tovio semantic diff |
Capability tokens — TOKEN¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-TOKEN-001 (PathScopeViolation) |
13 | An agent targets a path outside its token's scope. Rejected before any policy is read — the policy is not revealed. | tovio agent show <token-id> |
TVO-TOKEN-002 (TokenExpired) |
13 | An operation uses a token past its expires_at. No grace period. |
tovio agent renew <token-id> |
TVO-TOKEN-003 (TokenRevoked) |
13 | A token (or a delegation ancestor) was revoked by its issuer. | tovio audit show --token <id> |
TVO-TOKEN-004 (OperationNotPermitted) |
13 | The op is not in allowed_ops — e.g. an agent attempting obliterate / policy:modify / tag:force. |
tovio agent show <token-id> |
TVO-TOKEN-005 |
13 | A sub-token requests a scope not provably contained in its parent (it narrows, never widens). | tovio agent show <parent-id> |
TVO-TOKEN-006 |
13 | agent show/renew/revoke was given an id not in the token index. |
tovio audit log, or re-issue. |
TVO-TOKEN-007 |
13 | agent renew was asked to renew a revoked or delegated token. |
tovio agent new <name> … |
TVO-TOKEN-008 |
11 | agent new/renew/revoke was run in a Simple repo with no identity. |
tovio init --mode team |
Storage — STORE¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-STORE-001 |
7 | An object does not re-hash to its claimed address — corrupt or tampered with. | tovio fsck |
TVO-STORE-002 (ObjectObliterated) |
7 | A read targets an obliterated object. Returns a typed tombstone, never a 404. | tovio audit show --object <hash> |
Sync — SYNC¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-SYNC-001 |
17 | sync cannot reach the relay (DNS / TLS / connection). Local work is never lost. |
tovio sync (retry) |
TVO-SYNC-002 |
17 | The peer speaks a wire-protocol version this client cannot negotiate. | Rebuild from source — no tovio self-update command ships. |
TVO-SYNC-006 |
17 | A --push-only sync is refused because the remote lane diverged from your tip; pushing would orphan the remote tip. Fail-closed — nothing is uploaded. |
tovio sync (reconciles on-lane, then the push fast-forwards) |
Locks — LOCK¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-LOCK-001 |
13 | lock on a path that already holds an active (non-expired) lock. Names the holder and the expiry. |
tovio lock list |
TVO-LOCK-002 |
13 | A land (or a change rebase / split / absorb) diverged a path declared lockable = exclusive. Unmergeable content is refused, never stored as a conflict. |
tovio lock <path> --exclusive --ttl 1h |
CLI usage — CLI¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-CLI-001 |
2 | An unknown command, subcommand, or path. Suggests the closest valid command. | tovio help |
TVO-CLI-002 |
2 | A destructive command (notably obliterate) was invoked without its full confirmation phrase; --yes is not accepted. |
Re-run with --confirm "obliterate <hash>" |
Op-log — OP¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-OP-001 |
0 | The cataloged result of tovio undo — states what it undid and offers redo. A success. |
tovio redo |
TVO-OP-002 |
19 | undo with an empty op-log, or redo with nothing to replay. |
tovio explain undo |
Migration — MIG¶
| Code | Exit | Fires when | Recovery |
|---|---|---|---|
TVO-MIG-001 |
21 | Reserved for a Git construct that one-way import cannot represent losslessly. | Correct/exclude the named construct, then rerun tovio git import. |
Phase-0 offline-core codes¶
The offline core wires the codes below. Each is rendered in the three-part standard.
| Code | Exit | Fires when | What → Why → Next |
|---|---|---|---|
TVO-CLI-003 |
2 | A command runs outside a repository. | "Not a TOVIO repository" → no .tovio here → tovio init |
TVO-CLI-004 |
2 | init where .tovio already exists. |
"A TOVIO repository already exists" → won't overwrite → tovio status |
TVO-CLI-005 |
2 | A lane name violates the naming rules. | "Invalid lane name" → allowed charset / shape → pick a valid name |
TVO-CLI-006 |
2 | switch / lane -d names a missing lane. |
"No such lane" → no live lane by that name → tovio lane |
TVO-CLI-007 |
2 | lane <n> where <n> already exists. |
"Lane already exists" → name taken → tovio switch <n> |
TVO-CLI-008 |
2 | .tovioignore fails to parse. |
"Invalid .tovioignore" → the offending pattern → fix it (gitignore syntax) |
TVO-CLI-009 |
2 | lane -d on the current lane. |
"Cannot delete the current lane" → HEAD would dangle → tovio switch <other> first |
TVO-OP-003 |
19 | commit with no tracked files / no changes. |
"Nothing to commit" → the reason → tovio status |
TVO-OP-004 |
19 | lane <n> before any commit. |
"Nothing to make a lane from yet" → no tip to point at → tovio commit -m … |
TVO-OP-005 |
19 | A land precondition fails (self-target, no commits, unrelated histories). |
"Cannot land" → the specific reason → tovio health |
TVO-STORE-003 |
7 | A referenced object is absent from the store. | "Object not found" → missing object → tovio fsck |
TVO-MIG-002 |
21 | git import into a repo that already has lanes. |
"git import needs a fresh repository" → would entangle history → import into a clean repo |
TVO-MIG-003 |
21 | git import from an empty / branch-less repo. |
"Nothing to import from Git" → no history → point at a repo with commits |
TVO-MIG-004 |
21 | Git bridge transport, credential, timeout, or translation failure. | "Git bridge failed" → the named remote operation did not complete → fix Git connectivity/credentials, then rerun tovio git bridge <remote> --bidirectional |
TVO-MIG-006 |
21 | Git bridge publish refused: the remote refs/heads/tovio/* advanced (non-fast-forward). |
"Git bridge publish refused" → the bridge never force-pushes → re-import, then reconcile the diverged lanes through review/land |
See also¶
- Command reference — the commands that emit each code.
- Global flags —
--json,--quiet, and how errors render in each mode. - Glossary —
conflict object,obliteration,path scope, and more.
Last reviewed September 9, 2026
Suggest an improvement to this page Not for security reports — see disclosure