Error codes
GENERATED from packages/lint/src/codes.ts — do not edit. Run
pnpm --filter @runbooks/lint generate after changing the registry.
Every message this catalog produces carries a code. §18.7 lets the message itself stay short — name the location, name the fix — because the code resolves here, where the rule is explained. A code with no entry would make that a promise the interface does not keep, so the registry is checked against what the source actually emits.
capabilities
capabilities/undeclared
Step needs a capability the runbook does not declare — §12
capabilities[] is a privilege request a client checks against its allowlist. A step reaching past it is either mislabelled or lying, and both end with rights nobody reviewed.
capabilities/redundant
Capability no step uses — §18.5
Over-declaration is how agents end up holding privileges nothing in the procedure needs.
capabilities/unanalyzable
Command cannot be analysed — §12
A step nobody could analyse is a step nobody has checked. Reported rather than passed, because a gate that treats "I could not tell" as "fine" is decoration.
graph
graph/start-not-unique
Not exactly one start — §6
A procedure with two beginnings has no defined first step, and a reader following it would have to choose one arbitrarily.
graph/unreachable
Step cannot be reached — §6
A step nothing leads to is either a missing edge or a step that should not be there. Both are worth knowing before an agent runs it.
graph/dangling-edge
Edge points at nothing — §6
A route to a step that does not exist is a path a run can take and then be lost on.
graph/decision-thin
Decision with fewer than two branches — §6
A decision with one outcome is a check written in the wrong shape.
graph/decision-not-exhaustive
Decision without a default — §6
An agent reaching an answer none of the branches cover would have to invent what happens next, which is the improvisation the graph exists to prevent.
graph/check-unrouted
Check with no failure route — §6
A check whose expectation does not hold has to go somewhere, or the supervisor decides for itself.
graph/destructive-unpaired
Destructive step with neither gate nor rollback — §6
The rule the whole product exists to enforce: destructive work is always paired with a human decision before it or a way back after it.
graph/unbounded-cycle
Loop that is not a bounded retry — §6
An unbounded loop in an autonomous agent costs money and causes incidents until somebody notices.
graph/implicit-terminal
Step that ends by running out — §6
A graph may not end by having nowhere to go: a run that stopped somewhere undeclared cannot say whether it succeeded.
graph/step-without-tool
An action or check that names no tool — §6
A reader has to guess what performs it and a supervisor has nothing to scope. Not blocking — a wait, a decision and an escalate have nothing to invoke — but a procedure that does not say what it does is worth less.
graph/branch-check-without-expect
A branch leading to a check with no expectation — §6
The branch asked a question and the step it leads to does not say what answer would mean it went right, so the reader who took that branch cannot tell whether they are still on the path they chose.
graph/too-many-nodes
More than twelve steps and nothing decomposes it — §18.6
Past twelve a graph stops being readable at a glance, which is the only reason to draw one.
ids
ids/duplicate
Duplicate step id — §5
Every reference to the id becomes ambiguous, including the one a supervisor follows at run time.
injection
injection/instruction-override
Overrides the reader's instructions — §12
Catalog content is untrusted input for the agent reading it. A step describes a procedure to whoever runs it; it does not address the reader.
injection/role-assignment
Assigns the reader a role — §12
Catalog content is untrusted input for the agent reading it. A step describes a procedure to whoever runs it; it does not address the reader.
injection/system-prompt-mimicry
Imitates a prompt boundary — §12
Catalog content is untrusted input for the agent reading it. A step describes a procedure to whoever runs it; it does not address the reader.
injection/output-coercion
Dictates how the reader should answer — §12
Catalog content is untrusted input for the agent reading it. A step describes a procedure to whoever runs it; it does not address the reader.
injection/tool-coercion
Tells the reader to skip an approval — §12
The one thing a runbook may never say. An approval exists because someone decided the step needed one.
injection/exfiltration
Instructs the reader to send credentials somewhere — §12
Catalog content is untrusted input for the agent reading it. A step describes a procedure to whoever runs it; it does not address the reader.
injection/hidden-content
Content a reviewer does not see — §12
A human reviewer reads the rendered text and a parser reads the source. Anything only one of them sees is worth flagging.
licence
licence/missing
No licence — §10.2
An SPDX identifier decides whether a record may be indexed at all, and whether it may be adapted.
licence/unrecognised
Licence not recognised — §10.2
Whether the licence permits adaptation cannot be decided automatically, so a human classifies it once rather than the pipeline guessing permissive.
licence/not-indexable
Licence forbids indexing — §10.2
The terms are ones this catalog cannot satisfy by indexing the record at all. A non-commercial clause is here rather than under link-only because whether a catalog is commercial use is not a question we can answer on a publisher's behalf.
provenance
provenance/unobserved-state
The upstream is marked moved with no date for the observation — §3
changed and gone are claims that the source is not what it was, and without checked_at there is no moment at which anybody established that. A marker with no date is a claim about the present made at an unknown time; pnpm reindex writes the two together.
provenance/incomplete
Missing provenance — §10
Every record carries where it came from. Without it a record cannot be traced back, re-verified, or taken down.
publisher
publisher/confusable
A namespace that reads as another — §7
Two namespaces a person cannot tell apart is how a supply-chain substitution starts. The check is deliberately aggressive: a false positive costs a review, a false negative costs somebody's credentials.
relations
relations/dangling
Relation to a record that is not here — §22
A composition that cannot be assembled. The reader following the edge arrives nowhere, and the claim the record makes about its own dependencies is not checkable.
relations/unparseable
Relation reference is not a reference — §22
publisher/slug, optionally pinned. A reference nobody can resolve is a sentence, not an edge.
relations/must-pin
requires or rollback_of without a version — §22
Both assert what another record does, which is only true of a version: unpinned, the claim silently follows the other author's next edit.
relations/must-not-pin
part_of or escalates_to with a version — §22
Both are structural. Pinning freezes the arrangement, so a playbook keeps pointing at last year's version of its own step while every reference still resolves.
relations/cross-publisher
part_of across a publisher boundary — §22
Declaring yourself part of somebody else's procedure is a claim about their composition, made without them. The containing record decides what it contains.
relations/self
Relation to itself — §22
A record requiring, undoing, escalating to or being part of itself asserts nothing that can be acted on, and something was meant by it — most likely a reference to the record it was copied from.
relations/cycle
A cycle in a relation kind that cannot have one — §22
A requires cycle is a dependency nobody can satisfy first; an escalates_to cycle is the handoff loop escalation exists to prevent; a part_of cycle contains itself.
resolve
resolve/bad-ref
Not a reference this catalog can read — §9
A reference is publisher/slug@semver. Guessing at a malformed one would mean fetching something the caller did not ask for, and the caller would run it.
resolve/not-found
No such record at that reference — §9
The catalog holds no record at this address. A URL here is permanent, so a miss usually means a rename — /v1/aliases.json says where a slug went — rather than a record that never existed.
resolve/hash-mismatch
The bytes are not the bytes that were signed for — §9
What arrived does not hash to what the reference pins. Running it anyway would mean running a document nobody verified, which is the one thing pinning exists to prevent.
resolve/unpinned
Reference names a version, not bytes — §14
A version follows the publisher's next edit. Everything downstream of a run — the report, the attestation, verify — binds to a content hash, so a run named by version alone is a claim about whatever that version has since become.
resolve/upstream-changed
The source document has changed since it was adapted — §10
The upstream this record was derived from no longer says what it said. The record is not wrong, but its provenance no longer describes the thing it came from, and a reader deciding whether to trust it needs to know that.
resolve/upstream-gone
The source document is no longer reachable — §10
Provenance that cannot be checked is provenance a reader has to take on faith. The record stands; what it can no longer do is prove where it came from.
run
run/gate-refused
A person refused the gate, and said why — §13
Somebody looked at the approval request and declined it. Distinct from a request that expired: an expiry is a question nobody answered, and both end the run, so the difference is carried by this event and the reason it names. An approval needs no justification — proceeding is what the runbook asked for — but a refusal is a departure from it, and the next person at this step is owed the why.
run/gate-expired
Nobody answered the gate, and the run stopped — §13
The other way a gate ends without a yes. "Proceed on timeout" is the convenient default somebody eventually adds, and it turns every gate into a delay: a question nobody answered is not permission. The run is aborted rather than continued, and this event is what separates that from a refusal, which carries a person and a reason.
run/started
The run began at the declared entry — §13
Recorded rather than assumed: a run whose beginning is not in the record cannot be replayed, and a report about it cannot be checked against the graph it claims to have followed.
run/no-entry
The runbook declares no entry step — §6
A graph with no start has no first step, so there is nothing to authorize and nowhere to begin. This is caught before anything runs rather than at the first call.
run/input-missing
A declared input was not supplied — §13
Inputs are part of what a step's command means. Starting without one means the first command is assembled from a blank, and a blank in a path is a different path.
run/input-type
An input is not the type the runbook declares — §13
A declared type is a claim the author made about what the command will receive. Coercing it here would substitute this supervisor's judgement for theirs.
run/input-undeclared
An input the runbook does not declare — §13
An input nobody declared is an input nobody reviewed, and it reaches a command. Refusing it is cheaper than working out afterwards what it interpolated into.
run/entered
The run moved to a step — §13
Every transition is on the record, because the sequence of steps actually entered is what a report is a report about — and what an approval, an expectation and a rollback are all indexed by.
run/permitted
The call was authorized for this step — §13.2
Authorization is per step, never per run: a permission granted for the union of a runbook is a permission the agent holds while doing something else entirely.
run/out-of-order-step
That step does not follow this one — §6
The graph decides where a run goes, not the agent. A jump the document does not describe is a path nobody reviewed, and every guarantee here is about the paths that were.
run/after-end
A call arrived after the run ended — §13
A run that has ended has no current step, so there is nothing to scope a permission to. A client told only 'denied' would keep proposing into a run that is over.
run/unanalyzable-command
What this command invokes cannot be determined — §12
A command whose invocations cannot be read cannot be attributed to a declared capability, so it cannot be bounded. "I could not tell" is not permission.
run/approval-required
This step stops for a person — §13.4
A destructive or irreversible step is released by a human decision, and the run holds until there is one. The gate is the point at which somebody accepts what happens next.
run/approved
A person released the gate — §13.4
Recorded with who decided, because an approval nobody can attribute is an approval that cannot be reviewed afterwards — and the agent may never be that person.
run/expectation-failed
The step did not do what it said it would — §13
A postcondition is the author's statement of what the step was for. When it does not hold, continuing means running the rest of a procedure whose premise is already false.
run/expectation-unevaluable
The postcondition could not be decided — §13
Not knowing whether a step worked is a third state, and collapsing it into either 'passed' or 'failed' invents information. It routes to a person, not to a branch.
run/unchecked-advance
Leaving a step whose result was never decided — §13
At R2 a check is left by its result, not by proposal. Advancing past an undecided postcondition is how a run reports success for a step nobody established had happened.
run/unrouted-failure
The step failed and names nowhere to go — §6
Where a failure goes is the author's decision, and it was not made. Aborting is the only honest option: continuing would be this supervisor choosing a path the document does not contain.
run/budget-exhausted
The run reached its step budget — §13
A budget is what stops a retry loop from being unbounded. Reaching it ends the run rather than continuing quietly, because the interesting fact is that it was reached.
run/wall-clock-exhausted
The run reached its time limit — §13
Time is the bound that still applies when a procedure is waiting rather than acting. A run that outlives its window is one nobody is watching any more.
run/terminal-not-reached
The run stopped somewhere that is not an ending — §6
A procedure declares its terminals, and stopping anywhere else means the run is incomplete however it looked. A report says so rather than recording it as done.
run/ended
The run ended — §13
With the outcome it ended on, because 'it finished' and 'it succeeded' are different facts and a report that conflates them cannot be aggregated into anything true.
run/adjudicated
A person decided what the supervisor could not — §13.4
An unevaluable postcondition is resolved by a human, never by the agent whose work is in question. Recorded with who decided, for the same reason an approval is.
run/nothing-to-adjudicate
There is no undecided result to rule on — §13.4
Adjudicating a run that is not waiting on one would let a decision be recorded against a step that never asked for it, which is a signature on the wrong document.
runtime
runtime/profile-too-low
The client enforces less than the runbook requires — §13.3
A procedure that outruns its supervisor is the failure the execution contract exists to prevent. This is a refusal rather than a warning, because something ranked low still gets clicked.
runtime/coverage-insufficient
The adapter cannot see every tool call — §13.3
An adapter that cannot observe a call cannot bound it, so a destructive procedure under one is unsupervised in exactly the places it matters. A proxy does not see shell access; a tool-call hook does.
runtime/risk-above-ceiling
The procedure is riskier than this client allows — §13.3
The ceiling is a standing decision about what this client may be asked to do. Raising it is a deliberate act, which is the difference between a limit and a suggestion.
runtime/autonomous-unsupervised
Autonomous execution under a client that enforces nothing — §13.3
Agent-autonomous execution needs at least R1 — an allowlist, per-step scope and approval gates. Autonomy without them is not supervision that is lenient; it is no supervision.
runtime/capability-unavailable
A capability the runbook needs is not offered — §12
A record is runnable only if every capability it declares is available, never any of them: the other reading hands an operator a procedure they can start and cannot finish, and part of a destructive procedure is a state nobody designed.
schema
schema/invalid
Does not match the published schema — §5
The schema is what a third party validates against without our code. A record that fails it has no agreed meaning.
secrets
secrets/aws-access-key
AWS access key id — NG3
A credential in a published runbook is a credential in every cache that mirrors us. Replace it with a declared input.
secrets/aws-secret-key
AWS secret access key — NG3
A credential in a published runbook is a credential in every cache that mirrors us. Replace it with a declared input.
secrets/github-token
GitHub token — NG3
A credential in a published runbook is a credential in every cache that mirrors us. Replace it with a declared input.
secrets/slack-token
Slack token — NG3
A credential in a published runbook is a credential in every cache that mirrors us. Replace it with a declared input.
secrets/private-key
Private key — NG3
A credential in a published runbook is a credential in every cache that mirrors us. Replace it with a declared input.
secrets/jwt
JSON web token — NG3
A credential in a published runbook is a credential in every cache that mirrors us. Replace it with a declared input.
secrets/connection-string-password
Password in a connection string — NG3
A credential in a published runbook is a credential in every cache that mirrors us. Replace it with a declared input.
secrets/generic-high-entropy
High-entropy string with no recognised format — NG3
A credential in a published runbook is a credential in every cache that mirrors us. Replace it with a declared input.
secrets/private-address
Private network address — NG3
Not a credential, but a detail of your infrastructure that does not need publishing.
secrets/cloud-account-id
Cloud account identifier — NG3
Not a credential, but a fingerprint of the environment the procedure runs in.
signature
signature/invalid
Signature does not match this record — §8
The record changed after it was signed, or the signature is not this publisher's. Publishing it anyway would put the catalog's name behind a claim it checked and disbelieved.
signature/revoked
Signed with a revoked key — §8
A retired key verifies what it signed before it was retired and nothing after; a compromised key verifies nothing at all, because whoever holds it can date a forgery however they like.
signature/unverifiable
Key not published, so the signature could not be checked — §8
Not evidence of forgery: it says the key is not where the publisher said it would be. It is a warning rather than an error because a key-distribution problem is not the record's fault — and it never counts as valid, which is the half that matters.
vocabulary
vocabulary/synonym
A facet value that is a synonym of a canonical one — §4
k8s and kubernetes as two values split every list in half, and the reader who filtered by one never learns the other half existed. The record is sound; the fix is mechanical; the silence would not be.
vocabulary/unknown
A facet value that is in no vocabulary — §4
Not an error: an author with a system nobody has heard of should not be stuck, which is what keywords[] is for. But an unlisted value is not filterable, and the author is the only person who can say whether it should be.
yaml
yaml/unparseable
The document is not valid YAML — §18.3
Reported by the constructor's text view at the line it happened, under the same code the rest of the editor uses — one problem does not get two names. The text stays on screen: an editor that becomes unusable when a document does not parse is one you cannot fix a document in.