Exit codes
Every non-zero exit writes one diagnostic line per failure to stderr, and the last line is always
ERROR_CODE=<slug>. Branch on the exit code (or the slug), not on the message text — the message
can change wording; the slug does not. Content and a non-zero exit are not exclusive: a partly
successful show or a batch that landed some of its edits prints what it did, then still exits
non-zero, so stdout stays worth reading even on failure.
| Exit | Slug | Means | Seen from |
|---|---|---|---|
| 0 | — | done | all verbs |
| 1 | not_found |
a target, --old, or a pattern matched nothing; the nearest candidate is shown where one exists |
show, edit, transform, find |
| 1 | over_cap |
find had more than 50 hits (or --cap’s value) and printed the top-files map instead |
find |
| 1 | invalid_pattern |
the regex does not parse | find |
| 1 | exists |
write would overwrite an existing file; pass --force |
write |
| 1 | empty_input |
write got empty stdin; pass --empty |
write |
| 1 | empty_file |
edit’s --old matched nothing because the target file is empty; write it first with lets write --force |
edit |
| 1 | mixed_endings |
--old spans more than one line and the file mixes CRLF and LF; match one line at a time, or type \r\n literally |
edit |
| 1 | no_grammar |
a #symbol target on a file whose extension has no bundled grammar and no plaintext match; the message names the extension and a fallback target form |
show, edit, transform |
| 1 | update_available |
update --check found a newer release; nothing was installed |
update |
| 1 | path_conflict |
hooks install or update: a different lets comes first on PATH; named in the message, nothing written |
hooks install, update |
| 1 | not_on_path |
hooks install: no lets reachable on PATH at all |
hooks install |
| 1 | no_repository |
update: this build names no release repository |
update |
| 1 | guessed_span |
edit --insert-after targeted a plaintext-heuristic symbol whose span end is a guess, not a parsed boundary; use --insert-before or anchor on the last line |
edit |
| 2 | ambiguous |
the target, --old, or an attribute selector matched more than once; every candidate listed as path:line, at most 20 with (+N more) |
show, edit, transform |
| 2 | expect_refused |
--expect can’t confirm a multi-line range, or its content didn’t match what’s on disk |
edit |
| 3 | check_failed |
the guardrail failed after the edit; the file was reverted and is unchanged | edit, transform |
| 4 | over_budget |
content exceeded --max-bytes and no --budget was given |
show, find |
| 5 | changed |
the file changed since the --if sha:… it was given |
edit, transform |
| 6 | outside_tree |
a target or write is outside the working tree; pass --allow-outside |
show, find, edit, transform, write |
| 7 | unsupported_file |
binary, hardlinked, non-UTF-8 in the matched region, over --max-file-bytes, a directory given as a target, or (for transform) not a structured format, or a key that can’t be changed in place |
show, edit, transform |
| 7 | locked |
another lets process holds the file’s lock past the 2-second retry |
edit, transform, write |
| 7 | read_only |
the file lacks the owner-write bit; chmod u+w is the fix named in the message |
edit, transform, write |
| 7 | io_error |
any other I/O failure; a stdout write that fails because the reader closed the pipe is exit 0, not this | edit, transform, write, hooks |
| 7 | update_failed |
update: no release for this platform, the download failed, or the installer exited non-zero |
update |
| 8 | partial_batch |
some files of a batch landed; the footer names which | edit, transform |
| 64 | usage |
the command line, or a batch’s --from - input, is malformed — an unknown JSONL key, a short --if, an unrecognized --check preset, find -v, two conflicting stdin flags |
all verbs |
A missing file reaches exit 1 (not_found) whichever way it failed, so a caller can branch on the
code alone without parsing the message. Exit 64 is reserved for a malformed request — never exit
2, which is reserved for an ambiguity or a content refusal about what’s already on disk.
update --check (report only, no not_on_path/path_conflict refusal applies since nothing is
written) and hooks uninstall (exit 7 io_error if the settings file can’t be read or written,
leaving it untouched) are not in this table’s “seen from” column separately; they share the exits
above their sibling command uses.