Skip to content

Exit codes & PR annotations

This page is the contract between docmeta and your pipeline: what each exit code means, what each output format looks like, and when color is emitted. If you are wiring docmeta into a gate or parsing its output, this is the reference to build against.

docmeta validate returns one of three exit codes. Every CI platform treats a non-zero exit as a failed step, so the gate is enforced without extra scripting.

Code Meaning When it happens
0 Success Every validated file satisfies its schema set.
1 Validation failures One or more files failed validation. The run completed; the documents are non-conformant.
2 Operational or usage error docmeta could not complete the run — for example, no inputs and no config, an unknown --format, an unreadable config file, or an unexpected internal error.

The distinction between 1 and 2 matters in a pipeline. Exit 1 is a result: docmeta ran and found problems your authors must fix. Exit 2 is a failure of the run itself: a misconfiguration or environment problem you must fix. A gate that treats every non-zero exit the same will still block bad metadata, but separating the two lets you tell “the docs are wrong” from “the check is broken.”

docmeta validate supports three output formats, selected with -f / --format. The default is pretty.

Format Audience Machine-readable Available on
pretty Humans, local runs No validate, get, schemas
json Dashboards, scripts, tooling Yes validate, get, schemas
github GitHub Actions PR annotations Yes (one line per error) validate only

The default. A check mark or cross per file, indented error lines beneath each failing file, and a summary line. Intended for humans reading a terminal or a CI log.

Terminal window
$ docmeta validate bad-timestamp.md
✗ bad-timestamp.md
/timestamp must match format "date-time" (line 4) [google:okf:0.1]
1 file checked, 0 passed, 1 failed, 1 error

Passing files show as ✓ <file>. Add -q / --quiet to hide passing files and show only failures plus the summary. The summary line reports <n> files checked, <n> passed, <n> failed, <n> errors.

A single JSON object printed to stdout, suitable for dashboards or further processing. The top level has a summary and a results array.

Terminal window
$ docmeta validate bad-timestamp.md --format json
{
"summary": {
"files": 1,
"passed": 0,
"failed": 1,
"errors": 1
},
"results": [
{
"file": "bad-timestamp.md",
"format": "markdown",
"ok": false,
"schemas": ["google:okf:0.1"],
"errors": [
{
"schema": "google:okf:0.1",
"instancePath": "/timestamp",
"message": "must match format \"date-time\"",
"line": 4
}
]
}
]
}

The shape is stable:

  • summary — counts across the whole run.
    • files — number of files checked.
    • passed — files that satisfied every schema in their set.
    • failed — files with at least one validation error.
    • errors — total individual validation errors across all files.
  • results — one object per file.
    • file — the file path as docmeta saw it.
    • format — the extractor that read the file (for example markdown).
    • oktrue when the file passed every schema in its set.
    • schemas — the schema ids the file was validated against.
    • errors — the individual violations for this file (empty when ok).
  • Each error — one schema violation.
    • schema — the schema id that produced the error.
    • instancePath — a JSON Pointer to the offending field ("" for the document root, "/tags/0" for the first item of tags).
    • message — the human-readable reason.
    • line — 1-based source line, when docmeta can locate it. Omitted when unknown — for example, errors at the document root often have no specific line.
    • col — 1-based column. This field is defined in the output contract and is emitted when present, but the current extractors resolve errors to a line only, so col is absent in practice today. Treat it as optional.

One GitHub Actions workflow command per error. GitHub renders each line as an annotation pinned to the file and line in the pull request diff. There is no summary line and no per-file heading — only error lines, one per violation. A passing run prints nothing.

The format of each line is:

::error file=<file>,line=<line>,col=<col>::[<schema>] <field> <message>
  • file= is always present.
  • line= is included only when the error has a known line.
  • col= is included only when the error has a known column. Current extractors resolve to a line only, so col= is absent in practice today.
  • <schema> is the schema id that produced the error.
  • <field> is the JSON Pointer to the offending field, or (root) when the error is at the document root.
  • <message> is the human-readable reason.

A concrete line — a malformed timestamp field that fails the date-time format:

::error file=bad-timestamp.md,line=4::[google:okf:0.1] /timestamp must match format "date-time"

When the line is unknown, line= is dropped and only file= remains. A required-property error at the document root reports the field as (root):

::error file=missing-type.md::[google:okf:0.1] (root) must have required property 'type'

docmeta follows clig.dev conventions: primary output goes to stdout, diagnostics go to stderr, and color is emitted only when it is safe to do so. Color affects only the pretty format — json and github are always plain text so they parse reliably.

docmeta decides whether to emit ANSI color like this, in order:

  1. --no-color — passing this flag disables color unconditionally.
  2. NO_COLOR — if the NO_COLOR environment variable is set to any non-empty value, color is disabled.
  3. TTY detection — otherwise, color is emitted only when stdout is an interactive terminal (a TTY).

In practice this means color turns off automatically in CI, because a piped or redirected stream is not a TTY. You rarely need --no-color in a pipeline, but it is available when you want to force plain output — for example, when capturing a log that a downstream tool will read.