Skip to content

verb failure recovery

You ran a verb and got failed (or --apply errored before emission). This runbook is the operator-shaped recovery playbook, mapped to the errors[].code returned in the output envelope.

The fleet-shaped version of this catalogue is at /integrations/example-failure-modes/. That page is for fleet authors writing automated retry logic; this page is for the human at the terminal.

verb returned failed?
├── error code in output envelope?
│ │
│ ├── AUTH_* → set credentials (see /runbooks/auth-setup/)
│ ├── REPO_* → registry mismatch — fix registry.yaml
│ ├── FIELD_* → input JSON malformed — fix and retry
│ ├── FLEETS_* → fleet not allowed — see below
│ ├── PROFILE_* → wrong verb for repo profile — switch verb
│ ├── DIAGNOSIS_* → propose_fix needs a fresh diagnose
│ └── (other) → see per-code section below
└── no error code, just exit code 2?
→ unhandled exception. Report as bug with the JSON output.

Cause: --apply invoked without credentials.

Fix:

Terminal window
export PETROVA_GITHUB_TOKEN="$(op read 'op://PETROVA/PETROVA_GITHUB_TOKEN/credential')"
# or set the App env triple

Re-run.

Cause: target slug not in registry.yaml.

Fix: Either you typoed the slug (rerun with correct one) or the repo isn’t governed yet. For onboarding, see /runbooks/onboard-repo/.

Cause: read verbs (diagnose, validate) couldn’t find the local clone at $PETROVA_WORKSPACE/<slug>.

Fix:

Terminal window
cd $PETROVA_WORKSPACE
git clone https://github.com/<owner>/<repo>.git <slug>

Or set PETROVA_WORKSPACE to the directory where you cloned it.

Cause: input JSON fails schema validation.

Fix: the error message names the offending field path (e.g. /params/slug). Look at the verb’s schema in spec/verbs/<verb>.schema.json to see the expected shape, fix the JSON, retry. The skill recipe (skills/petrova-act/verbs/<verb>.md) has worked examples.

Cause: invoked verb with --actor fleet:<id> but <id> not in the target repo’s fleets_allowed list.

Fix: Either:

  • (a) Run as a human (drop --actor or use human:<email>).
  • (b) Open a request_review PR against petrova-hq/registry.yaml adding the fleet to the relevant repo’s fleets_allowed. Wait for merge, then retry.
Terminal window
# Composing the registry edit:
cd ~/code/workspace/petrova-hq
$EDITOR registry.yaml # add fleet ID to fleets_allowed for target repo
# Then propose via verb (yes, you can use the verb to edit the verb's gate):
petrova request_review petrova-hq --input /tmp/registry-edit.json

Cause: request_merge_when_green against a strict-profile repo (or standard without explicit fleet allow).

Fix: switch to request_review instead. Same input minus merge_method. Human approves the PR.

Cause: close_phase has a friction item with classification: deferred but no deferred_to_milestone.

Fix: add deferred_to_milestone: M<phase>.<n> referencing a later phase, retry.

Cause: close_phase invoked without sign_off.human populated.

Fix: the human countersigning this close needs to add their identity. Edit input JSON:

"sign_off": { "human": "your-name your@email 2026-04-29" }

Cause: propose_fix references a diagnosis ID that’s missing from the local cache, expired (> 24h old), or was for a different repo.

Fix:

Terminal window
petrova diagnose <slug> --json | jq -r '.result.diagnosis_id'
# Use this fresh ID in /tmp/fix.json, retry propose_fix.

Cause: input includes a path matching the privileged-path denylist (.github/workflows/, *.env, secrets/, deploy/credentials/).

Fix: don’t retry as the same verb. Privileged path edits are operator-only. Make the change manually via direct PR (with human review), separate from the verb pipeline.

Cause: target repo’s default_branch (per registry.yaml) doesn’t exist on GitHub.

Fix: registry data drift. Update the registry entry’s default_branch field to match reality (typically renamed mastermain).

Cause: --apply after a previous --apply partially succeeded (branch created, some files committed, then died). The retry tries to create a file that already exists on the branch.

Fix:

  • Easy path: delete the partial branch on GitHub, retry.
  • Surgical path: change the verb’s input slightly (e.g. different decision slug), which produces a new idempotency key and a new branch.

422 Unprocessable Entity (concurrent edit)

Section titled “422 Unprocessable Entity (concurrent edit)”

Cause: between dry-run and apply, the default branch’s SHA moved (someone merged something).

Fix: simple retry — the emitter fetches a fresh SHA on each apply attempt.

If a verb fails with an error code not in this table, or with no error code (just exit 2), capture:

Terminal window
petrova <verb> <repo> --input <input> --apply --json > /tmp/petrova-error.json

Open a finding doc:

docs/findings/YYYYMMDD-HHMM-petrova-verb-failure-<slug>.md

Include the JSON output, the input file contents, and what you expected. Either fix it yourself (verb implementations live at cli/src/verbs/) or surface to the operator.