How to read a TOVIO error code

The TVO-AREA-NNN shape, the exit-code classes, and where the remediation lives.

Written By Dustin

Last updated 16 minutes ago

Every error ends with a code shaped TVO-<AREA>-<NNN>.

  • TVO — fixed prefix.
  • AREA — the subsystem: PERM, KEY, SYNC, TOKEN, LOCK, STORE, MIG, PLUGIN, MCP, CONFLICT, and others.
  • NNN — a stable number. Numbers are never reused; a retired code is tombstoned with a pointer to its replacement.

Because codes are stable, TVO-PERM-001 means the same thing across versions and is worth searching for directly.

Errors are meant to be actionable

Every error has three parts, always in this order:

  1. What happened — one plain-language line naming the concrete object or path. No code in the first line.
  2. 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.
  3. What to do next — concrete steps, most-likely-first, each with a runnable command where one exists.

A permission, key, or crypto error is never a bare "denied". If an error does not tell you what to do next, that is treated as a bug worth reporting.

Exit codes: one class per area

Exit codes are deliberately coarse, so a script can branch on category while the precise code stays in --json.

ExitClassAreas
0Success — and note that creating a conflict is success
1Generic / uncategorized failurefallback, plus operational WS failures
2CLI / configuration usage error (bad flag, unknown command, missing confirmation)CLI, selected WS
7Storage / integritySTORE
11Crypto / key materialKEY, CRYPTO
13Permission / policy / capability denialPERM, TOKEN, LOCK
17Sync / wire protocolSYNC
19Change / op-log (undo, redo, history)OP
21Migration / Git bridgeMIG

The mapping is per area, not per code, and a handful of codes are deliberate exceptions — TVO-TOKEN-008 and TVO-TOKEN-011 both exit 11 rather than 13, because what failed is key material. Branch on the code when you need to be exact.

For scripts

Pass --json and branch on error.code, never on message text or on the numeric exit alone:

{  "error": {    "code": "...",          // the stable code — the primary key    "area": "...",          // subsystem, derived from code    "category": "...",      // semantic category, e.g. authorization    "exit_class": "...",    // symbolic form of exit_code    "retryability": "...",  // never | after-user-action | transient    "title": "...",         // the What    "cause": "...",         // the Why    "remediation": [ ... ], // ordered; each has text and an optional command    "context": { ... },     // code-specific facts    "exit_code": 13,    "docs": "..."           // optional per-code deep link  }}

Do not infer retryability from the area or the exit code — read the retryability field, which is the only place that answers it. An older envelope may omit area, category, exit_class, and retryability; a reader should tolerate that and derive them from the code and exit_code pair.

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. A context object never carries a value you are not authorized to read.

Full detail

Most codes have their own page, and the error prints the link on its last line next to the code:

[TVO-PERM-001]  https://tovio.dev/errors/TVO-PERM-001

Follow it straight from the terminal. The same link is carried in the optional docs field of the --json shape, and is absent for the few codes with no page of their own (the generic TVO-CORE-000 fallback among them). The error reference on the documentation site is the canonical index behind them.

Things that are deliberately not errors

There is no code for "the remote has work you do not have" or "diverged history". A divergent sync reconciles the lane into a two-parent merge-or-conflict commit and reports success. The one guarded case is a push-only sync over a diverged lane, which is refused rather than allowed to clobber the remote tip.