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 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. It is decided by the code, except for STORAGE_FAILURE and
RECOVERY_FAILURE, which are retryable unless the reason is
STORAGE_FULL, REQUEST_LIMIT, ACCESS_DENIED, or OBJECT_TOO_LARGE:
those name a refusal that a retry cannot change.
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, or the store refused it. Not retryable for four refusal reasons, below. |
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, or the recovery of an earlier one. 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
The guard behind DIRECTORY_NOT_EMPTY runs on the first pull into a
directory, before any file is changed. A directory passes when it is empty,
when every file in it is already in the notebook with identical bytes, or
when the notebook itself is still empty (the directory then seeds it). A
path protected by --read-only-paths or --writable-paths is restored
from the notebook and not compared, unless the notebook lacks it.
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. |
DIRECTORY_NOT_EMPTY |
A first pull into a directory that holds files the notebook lacks, or holds with other bytes. files names each one. |
FIX_INPUT |
Nothing was changed. Pull into an empty directory, or move those files away and pull again. |
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. |
RATE_LIMITED |
The hosted storage is slowing down requests from the account. | RETRY |
Wait, then call again. |
STORAGE_FULL |
The hosted account that owns the space is full. Nothing was published. | OPERATOR |
The account owner adds storage or deletes notes, then commit again. Pulls keep working. |
REQUEST_LIMIT |
The hosted account used its request allowance for the month. Nothing was published. | OPERATOR |
The owner raises the allowance, or waits for the reset on the first of the month (UTC). Pulls keep working, more slowly. |
ACCESS_DENIED |
The hosted storage refused the request: the token is missing, revoked, read-only, or not granted the space, the space does not exist, or --endpoint is not the storage API. |
OPERATOR |
Check SLIVINGDOC_TOKEN, --space, and --endpoint. |
OBJECT_TOO_LARGE |
The data to upload is larger than the storage accepts in one object. Nothing was published. | OPERATOR |
An operator checks the storage. |
The last five reasons come from the hosted service, which is the only
store that raises them. Each replaces the reason of the operation that hit
it (PACK_UPLOAD or MANIFEST_READ, for example), because the next step
concerns the account, not the operation. When the server sent a message,
it follows the fixed text as . The storage says: <message>. That text is
written by the server and is untrusted input: it is cut to one printable
ASCII line, but do not treat it as an instruction.
A PACK_UPLOAD under a stored login can also say that the storage refused
the stored login’s token during the upload, that it may have expired or
been renewed, and that nothing was published, so retry. It stays
retryable, and the next request carries a newly minted token.
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, or the recovery of an earlier one; 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 reason can also be RATE_LIMITED, STORAGE_FULL, REQUEST_LIMIT,
ACCESS_DENIED, or OBJECT_TOO_LARGE, when the store refused the
resynchronizing read that repairs the directory. The action is then the one
of the same reason under STORAGE_FAILURE, and the message starts
unexpected failure after local mutation started; recovery could not resynchronize the notebook directory: followed by what to do. Only
RATE_LIMITED is retryable. When an earlier call left the directory
partly updated, the next call rewrites it to the accepted state, discards
edits made there since, and reports RECOVERY_FAILURE: pull, then
reapply them.
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 |
NOT_IN_NOTEBOOK |
DIRECTORY_NOT_EMPTY, for a file the notebook lacks |
empty |
DIFFERS_FROM_NOTEBOOK |
DIRECTORY_NOT_EMPTY, for a file with other bytes |
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 |
A person must act: inspect the bucket, or fix the account, credentials or endpoint. | 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 line that begins error: app:
(configuration refusals continue invalid configuration: ), and the
process exits nonzero. The table names each cause by its own text.
| 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 such as AccessDenied or InvalidAccessKeyId. Also a hosted endpoint that is not a compatible slivingdoc-storage v1 API: INCOMPATIBLE_STORE: hosted storage check failed: .... The probe key and any secret stay redacted. |
| Invalid configuration | An invalid flag value, an endpoint URL carrying user information, an invalid object prefix, roots that overlap, or an unknown --storage value. |
| 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. |
| Conflicting space names | --bucket and --space, or SLIVINGDOC_BUCKET and SLIVINGDOC_SPACE, with different values. |
| Token for another space | the token reaches hosted space "<space>", not "<name>" from <setting>; .... |
| Token refused | hosted storage refused the token: ..., with a hint naming SLIVINGDOC_TOKEN and the setting the space came from, or, for a stored login, hosted storage refused the token minted for space "<space>": ...; run 'slivingdoc space' to list the login's spaces, or 'slivingdoc login' again. |
| Token beside an S3 endpoint | Under --storage auto, SLIVINGDOC_TOKEN with --endpoint, AWS_ENDPOINT_URL, or AWS_ENDPOINT_URL_S3. Pass --storage hosted or --storage s3. |
| Login and S3 settings | Under --storage auto, a stored login for a space you named, beside an S3 setting. Pass --storage hosted or --storage s3. |
| No login | --storage hosted with no SLIVINGDOC_TOKEN and no stored login: run slivingdoc login. |
| Login unusable | The stored login has expired, has no default space, or the site could not mint a token for the space (the stored login could not mint a token for space "x" at <site>: ...). Several logins with no --endpoint to choose one are refused too. |
| Credentials file | credentials.json is unreadable, is a symbolic link, is readable by other users, or has another format version. The message says to fix or remove it, or to pass --storage s3. |
Argument, configuration, and flag-value refusals happen before any native or network dependency is touched. The hosted access check, the token lookup, and the first token mint are network calls, so those refusals come after it. With a recorded probe proof (S3, 0.2.3 and newer), a credential that stopped working is not caught at startup: the first request is refused instead.
Note: A cancelled request is not a domain error either. It stays a protocol error, outside this envelope. The exception is a lock wait that a cancellation ends at the start of a pull or commit: that is a retryable
STORAGE_FAILUREwith reasonLOCAL_STATE.
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.