Docs · ODIF
◍ ODIF
The oam Diagnostic Interchange Format: one versioned JSON Lines envelope for everything oam reports — parse errors, type errors, runtime exceptions, unhandled rejections, test results, install events.
Getting it
Pass --json before the subcommand. Diagnostics go to
stderr, one JSON object per line; your program's own
output is untouched on stdout.
oam --json run app.ts
oam --json test
oam --json check
oam --json install
A real line, from a program that throws:
{"odif":"1","code":"OAM-RT0005","severity":"error","origin":"runtime",
"message":"app.ts:1\nthrow new Error(\"boom\");\n ^\n\nError: boom\n ...",
"docs":"https://oamjs.org/docs/errors#OAM-RT0005"}
Parse line by line and skip what you do not recognise. A line that is not valid JSON is not an ODIF diagnostic — a panic, for instance — and should be kept rather than dropped, because a non-zero exit with zero diagnostics has to be explainable by something.
The envelope
| Field | Type | Meaning |
|---|---|---|
odif |
string |
Envelope version, always "1" today. Gate on it: a
future version may add or move fields.
|
code |
string |
The stable namespaced code, e.g. OAM-RT0005. See
the code reference.
|
severity |
string | "info", "warning" or "error". |
origin |
string |
Which subsystem raised it, and therefore which code namespace
applies: "parse", "typecheck",
"resolve", "runtime",
"test", "install".
|
message |
string | The human-readable detail. May be multi-line — an uncaught exception carries its whole Node-format report here. |
spans |
array | Source locations. Omitted when empty. |
docs |
string | The published page for this code, anchored at it. Omitted when absent. |
related |
array | Reserved. See below. |
fingerprint |
string | Reserved. See below. |
repairs |
array | Reserved. See below. |
Spans
A span names a file, a start and an end. Lines and columns are both 1-based.
{"file":"src/a.ts","start":{"line":2,"col":1},"end":{"line":2,"col":6}}
For a transpiled file the span is the position in your source, not in the generated JavaScript. (The debugger is the one place that does not hold — see the inspector page.)
Reserved fields
related, fingerprint and repairs
are part of the v1 envelope and are declared in the schema, but nothing
in oam populates them yet, so they are absent from every line oam emits
today. They are documented here so a consumer can be written to ignore
them safely rather than to depend on their absence.
When they do arrive, the shapes are fixed:
-
related— an array of spans, same shape asspans, for secondary locations such as "declared here". -
fingerprint— a string that stays the same across runs for what is recognisably the same problem, for deduplication and flake correlation. -
repairs— an array of{"id","safety","edits","note"}, wheresafetyis"safe-auto"(mechanical and semantics-preserving),"review"(plausible, needs a human) or"unsafe-hint"(a hint, never an edit plan), and each edit is{"span","new_text"}.
Reading it as a program
Two conventions make ODIF worth consuming over scraping stderr. Codes
are stable, so matching on code survives message rewording.
And severity is authoritative: OAM-TEST0000, the
oam test summary, is "info" when everything
passed and "error" when anything did not, so a consumer can
gate on severity without parsing counts out of prose.
oam's own MCP server does exactly this —
claude mcp add oam -- oam mcp runs a file, splits stderr
into parsed diagnostics and unparseable raw lines, and hands an agent
both.