Compiler failures use a diagnostic code, source span, and message. With --diagnostics=json, compiler tools, the package launcher, and formatter emit one JSON object per line on their ordinary failure paths:

{"schema_version":2,"severity":"error","code":"check.type","message":"...","path":"main.zi",
 "line":1,"column":1,"end_line":1,"end_column":1}

The code identity is the stable part. Messages and spans can improve; do not parse human-readable message text when a code is present. Code names use a phase prefix such as parse, check, emit, module, package, law, parallel, gpu, zx, zir, vm, or host.

Query remediation with:

ziran explain check.slice_lifetime
ziran explain zir.noncanonical --json
ziran explain --list
ziran explain --list --json
ziran check --target=zx --diagnostics=json --root . main.zi

A single explanation has schema version 1:

{"schema_version":1,"stability":"stable-code","code":"...",
 "summary":"...","next_step":"..."}

The list output has the same schema version and stability field, with a codes array containing every code currently emitted by the compiler. The tests/explain.sh conformance test scans compiler diagnostic calls and fails if a new code is missing from the registry. Unknown codes exit nonzero rather than receiving a guessed explanation.

Diagnostics use only current schema version 2 and error or warning severity. Explanation objects retain their independent schema version 1. Each diagnostic is a complete UTF-8 JSON line of at most 32768 bytes. Messages retain at most 2048 input bytes at a UTF-8 boundary; truncation.message reports clipping. Optional metadata is omitted as a complete field or structure if it would exceed the record budget; truncation.metadata reports that omission. Paths over 1024 bytes become null rather than a misleading shortened path.

Initializer, assignment, argument, and return mismatches include checked expected_type and actual_type facts. Target restrictions supply target and capability when known. semantic_id identifies the enclosing checked function using the same signature-based identity as ziran api; null means unavailable. document is the raw source's captured SHA-256 digest, byte count, and coordinate_space (source or rewritten_source), or null for unavailable source identity, including saved IR. These facts are not inferred from messages. cause is null when unavailable; otherwise its fixed kind is type_mismatch, unresolved_name, unresolved_function, or target_restriction, with a checked subject when known. Facts are omitted only with explicit metadata truncation.

related_graph contains record-local nodes and directed edges. A checked type requirement has primary node 0, declaration node 1, and a requires_type edge; other relations may be null when the checker has only a related location. Empty graphs mean no related fact is available.

Missing-name diagnostics include suggested_name when a close visible name exists. alternatives groups independent repairs, each with an ID, message, and an array of exact, non-overlapping edits. The current checker offers at most one replace_name alternative. Each edit supplies zero-based half-open UTF-8 byte offsets, one-based line/byte columns, document_sha256, original, and replacement. Consumers must verify both the hash and preimage. Edits are emitted only after the lexer identifies one unambiguous identifier and the reread source matches the captured document. Strings, comments, ambiguous occurrences, rewritten source without a raw mapping, and saved IR receive no speculative edits. Replacements and preimages are never clipped to fit a budget.

Schema-1 diagnostic consumers must explicitly migrate to version 2: related is now related_graph, and top-level edits is now alternatives[].edits. No legacy diagnostic mode is retained.

check --target=c|go|py|zx checks target-specific foreign identities/imports and Go/portable scalar-union restrictions before writing output. Builds use the same preflight policy. Its failures include target and a capability such as ffi.go, types.go, or unions.scalar; individual lowerers and the portable verifier still check their remaining target limits.

ZIRAN_DIAGNOSTICS=json selects the same format. Format flags are read before argument validation, so an earlier invalid option still honors a later JSON flag. Invalid UTF-8 bytes in paths and subprocess logs are escaped; valid Unicode is retained. Native C compilation captures and drains both output streams in JSON mode, bounds the retained log, and reports truncation with zir_c.toolchain. Allocation failure reporting does not allocate another diagnostic buffer. tests/tool_diagnostics.sh covers these paths, missing launcher tools, invalid command arguments, and formatter errors.

Arbitrary external package tools and fatal process signals can still produce unstructured output. Consumers should reject unknown schema versions and preserve diagnostic ordering. Missing optional metadata means it is unavailable; consumers must not guess it from the human-readable message.