Errors
Every error code and every reason under it, with its meaning, its action, whether a retry helps, what to do next, the file reasons, and the startup refusals.
Every domain error carries three stable tokens: a code, a reason one
level more specific, and an action naming the caller’s next step. Every
file entry inside an error carries its own reason token. Message text can
change between releases; these tokens cannot, so branch on them rather
than on prose.
The retryable field is a property of the code, not of the reason. It
says whether repeating the same call can help — which is not always the
same as what action tells you to do next. PUBLICATION_UNPROVEN, for
example, is retryable but asks you to pull first.
The codes
| Code | Retryable | Meaning |
|---|---|---|
INVALID_REQUEST |
no | Invalid input, or a state the operation refuses before any Git or S3 work. |
CONTENT_CONFLICT |
no | A three-tree merge conflict, or marker blocks already present. The visible directory holds the full merged result with markers. |
STORAGE_INTEGRITY |
no | Stored state failed validation: a corrupt pack, a pack that contradicts its descriptor, a missing object in the accepted history, or a cache that cannot be trusted. |
STORAGE_FAILURE |
yes | An object-store operation failed without a known accepted result. |
REMOTE_BUSY |
yes | The conditional write lost the configured retry bound. Visible files are preserved for another attempt. |
RECOVERY_FAILURE |
yes | An unexpected failure after local mutation started. The generic recovery path ran, and the error carries its report. |
INCOMPATIBLE_STORE is a startup diagnostic and never appears in a tool
result; it is covered at the end of this page.
Reasons, by code
INVALID_REQUEST
| Reason | Meaning | Action | What you do next |
|---|---|---|---|
MALFORMED_INPUT |
Strict decode failure: an unknown field, a null, a wrong type, or a size bound. | FIX_INPUT |
Correct the request and call again. Unknown fields and explicit nulls are never accepted. |
PATH_OUTSIDE_ROOT |
The request path escapes, or is not below, the workspace root. | FIX_INPUT |
Address a directory at or below the workspace root the message names, or omit path entirely. |
MESSAGE_BLANK |
The commit message is only white space. | FIX_INPUT |
Send a real message. |
MESSAGE_TOO_LONG |
The commit message is over the byte bound. | FIX_INPUT |
Shorten it to at most 16,384 bytes. |
MESSAGE_INVALID |
The commit message is not valid UTF-8, or contains U+0000. | FIX_INPUT |
Send valid UTF-8 without the NUL character. |
PULL_REQUIRED |
A commit without a managed pull: there is no accepted baseline for this directory. | PULL |
Call notes_pull for that path once, then commit. No remote state was touched. |
INVALID_CONTENT |
A visible file violates the notebook contract; files names it. |
EDIT_FILES |
Delete or replace the named file — a binary, a symlink, or an invalid name — then call again. |
READ_ONLY_PATH |
The commit touched a protected path. The changed files were reset to the last accepted content. | EDIT_FILES |
Write where the process may write. The message and the trailers name the configured sets. |
CONTENT_CONFLICT
| Reason | Meaning | Action | What you do next |
|---|---|---|---|
MERGE_CONFLICT |
The three-tree merge conflicted; markers were written. | EDIT_FILES |
Resolve the marker blocks in the named files and line ranges, then commit again. |
UNRESOLVED_MARKERS |
The commit found complete marker blocks already in the files. | EDIT_FILES |
Remove the marker lines — including one you wrote by hand — and commit again. |
Remote state is unchanged in both cases. See Resolve conflicts.
REMOTE_BUSY
| Reason | Meaning | Action | What you do next |
|---|---|---|---|
RETRIES_EXHAUSTED |
The conditional write lost every attempt in the bound. | RETRY |
Call again. Your files are untouched. Sustained contention is a reason to raise --commit-retries or to split the notes across more files. |
STORAGE_FAILURE
| Reason | Meaning | Action | What you do next |
|---|---|---|---|
MANIFEST_READ |
current could not be read. |
RETRY |
Call again. A persistent failure is a credentials, endpoint, or connectivity problem. |
PACK_DOWNLOAD |
A referenced pack could not be downloaded. | RETRY |
Call again. |
PACK_UPLOAD |
The proposal pack could not be uploaded. | RETRY |
Call again. Nothing was published. |
PUBLICATION_UNPROVEN |
The conditional-write response was lost and acceptance could not be proved. | PULL |
Pull first and look at the result: your change may or may not have landed. Do not blindly republish. |
MANIFEST_WRITE |
The manifest write failed with a definite error other than a lost precondition. | RETRY |
Call again. |
LOCAL_STATE |
A private-state operation failed before any mutation. | RETRY |
Call again. Check the private root’s permissions and free space. |
INTERNAL |
An unrecognised error; the fallback mapping. | RETRY |
Call again, and read the server log for the diagnostic ID. |
STORAGE_INTEGRITY
Every reason here means stored state is not trusted, and every one asks for a person. Nothing is materialised and no state advances.
| Reason | Meaning | Action | What you do next |
|---|---|---|---|
MANIFEST_INVALID |
current failed strict validation. |
OPERATOR |
An operator inspects the bucket. Retrying cannot fix an invalid manifest. |
PACK_INVALID |
A referenced pack is missing, contradicts its descriptor, or fails import. | OPERATOR |
An operator inspects the bucket, and the deployment’s own recovery policy applies. |
HISTORY_INVALID |
The accepted history is missing an object or fails validation. | OPERATOR |
As above. |
ENGINE_FAILED |
A merge, commit, export, or snapshot read failed inside the engine. | OPERATOR |
Read detail when it is present, and the server log record with the same diagnostic ID otherwise. |
ENGINE_FAILED is the one reason that can carry an optional detail
field, and only when the engine classified the cause itself. The closed
set of descriptions is:
"<name>" is not a regular text file; the notebook stores UTF-8 text files onlyno changed files were found to publishpart of the stored notebook state is missing locally; run notes_pull again to restore itthe downloaded notebook state was emptythe notebook state to record was empty
A cause outside that set carries no detail at all, because free-form
engine text cannot be made safe by filtering. The full cause is in the
server log record for the same diagnostic ID.
RECOVERY_FAILURE
| Reason | Meaning | Action | What you do next |
|---|---|---|---|
LOCAL_MUTATION_FAILED |
A failure after local mutation began; recovery carries the report. |
PULL when resynchronized is true, otherwise RETRY |
Follow the action. The report’s remoteAccepted says whether the publication landed: yes, no, or unknown. |
The recovery report names the failed stage — a read-only reset that
fails during a commit reports stage commit.readonly, for example. A
successful repair never converts the anomalous call into OK, and the
visible directory may have been rebuilt from the accepted state.
File reasons
Each entry in an error’s files array carries its own reason:
| File reason | Used by | Ranges |
|---|---|---|
TEXT_CONFLICT |
MERGE_CONFLICT, for a text conflict |
marker ranges |
PATH_CONFLICT |
MERGE_CONFLICT, for a file-versus-directory conflict |
empty |
UNRESOLVED_MARKERS |
UNRESOLVED_MARKERS |
marker ranges |
READ_ONLY |
READ_ONLY_PATH |
empty |
INVALID_CONTENT |
INVALID_CONTENT |
empty |
Ranges are one-based, inclusive, ordered, and non-overlapping; a file
with no range carries an empty array. A file under a protected path never
carries TEXT_CONFLICT or PATH_CONFLICT, because pull takes the
remote’s side there before the merge runs — but a marker block written
there by the caller is still rejected as UNRESOLVED_MARKERS, since that
check runs before the path check.
Actions
| Action | Caller meaning | CLI wording |
|---|---|---|
FIX_INPUT |
Change the request, then call again. | correct the request, then call again |
EDIT_FILES |
Edit the visible files, then commit. | edit the files, then commit |
PULL |
Call pull, then continue. | pull, then continue |
RETRY |
Repeat the same call. | retry the same call |
OPERATOR |
Stored state is not trusted; a person must act. | operator attention needed |
Startup refusals
Some failures happen before any tool call, so they never reach the error envelope above. They are written to stderr as one redacted diagnostic, and the process exits nonzero.
| Refusal | Cause |
|---|---|
INCOMPATIBLE_STORE |
The S3 compatibility probe failed. The store does not enforce the three conditional-write guarantees, or the probe hit an operational error — an expired login, AccessDenied, InvalidAccessKeyId. When the cause is operational the diagnostic names it, while the probe key and any secret stay redacted. |
| Invalid configuration | An invalid flag value, an endpoint URL carrying user information, an invalid object prefix, or roots that overlap. |
| Invalid path-set entry | An absolute path, a .. or .git segment, or an entry over the length bound, in either path setting. |
| Overlapping path settings | One path named by both --read-only-paths and --writable-paths. The diagnostic names the path and both settings. |
| Argument refusal | A commit without -m, or more than one positional path. |
All of these happen before any native or network dependency is touched,
except INCOMPATIBLE_STORE, which is the probe itself.
Note: A cancelled request is not a domain error either. It stays a protocol error, outside this envelope.
Next
- MCP tools — the envelope these tokens travel in.
- CLI — how the same error is printed to a terminal.
- Logging and profiling — finding the server log record for a diagnostic ID.