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.

JSON is the source of truth. The human-readable output is a renderer over the same object, not a separate code path, so the pretty form can never say something the JSON does not.

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

FieldTypeMeaning
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:

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.