Errors and refusals
← Documentation index
Every error the API returns carries a code:
Most of them are refusals, not faults. This application would rather stop and
explain than guess about somebody's photographs, so a code below usually means it
protected something. Each row says what caused it and what to do.
Access and the trust boundary
| code |
cause |
what to do |
unauthenticated |
no application session |
reload the page; the browser bootstraps one |
access_denied |
wrong or missing access secret |
check PHOTO_PIPELINE_ACCESS_SECRET. Attempts are logged with the caller's address only |
too_many_attempts |
more than five failed secret attempts in a minute |
wait. This is the rate limit, not a lockout |
csrf_failed |
a mutation without the session's token |
reload; a stale tab has an old token |
host_not_allowed |
the Host header is not a configured name |
add the real hostname to PHOTO_PIPELINE_ALLOWED_HOSTS |
origin_not_allowed |
the request came from another origin |
not something a browser tab of this app produces |
cross_site_blocked |
another site triggered the request |
expected — this is the protection working |
payload_too_large |
the body exceeds PHOTO_PIPELINE_MAX_REQUEST_BYTES |
send less; every endpoint takes small commands |
path_not_allowed |
a path resolved outside the library roots |
usually a symlink. Nothing outside the roots is reachable, by design |
The safety gate
| code |
cause |
what to do |
dry_run_not_approved |
mutation is gated until a dry run is approved |
run dry-run, read it, then approve-dry-run |
approval_scope_mismatch |
the approval covers different library roots |
approve a report for the roots actually configured |
approval_unreadable |
the approval record cannot be read |
re-approve; do not edit it by hand |
Concurrency and staleness
| code |
cause |
what to do |
lock_held |
a mutating job already holds the lane |
wait for it. One at a time is what makes a crash recoverable |
version_conflict |
you acted on a version that changed underneath you |
the view re-renders with the server's truth; decide again |
invalid_transition |
a state change that the machine does not allow |
usually a stale tab; reload |
job_error |
the job service refused the request |
the message says why |
rename_recovery_required |
an interrupted rename is unresolved |
resolve it in Renames. Unrelated mutations stay blocked on purpose |
Stage refusals
| code |
cause |
what to do |
nothing_to_score |
no photo is eligible for safety scoring |
the queue is already decided |
nothing_eligible |
no photo is eligible for analysis |
resolve safety decisions first |
nothing_to_plan |
no approved album name to build a plan from |
approve a proposal first |
invalid_decision |
the decision is not one this cluster accepts |
reload the cluster |
invalid_proposal |
the name is empty, reserved, or has forbidden characters |
fix the name; the rules are in Album proposals |
unknown_album |
the album is not a folder the library knows |
rescan |
cannot_apply |
the plan is invalid, stale, or another plan is unresolved |
read the blockers in the preview |
cannot_rollback |
the operation is complete or its preconditions no longer hold |
rollback is recovery, not undo |
Upload
| code |
cause |
what to do |
stale_preflight |
bytes changed between approval and running |
re-run preflight; this is the check that stops the wrong bytes being uploaded |
not_runnable |
the batch is in a state that must not be re-run |
an uncertain batch is verified, never retried |
requires_verification |
the outcome is unknown; the server may hold the files |
verify against Immich, then resolve |
changed_after_upload |
the file changed after a successful upload |
uploading again may create or upgrade an asset |
Operations
| code |
cause |
what to do |
backup_failed |
the snapshot could not be taken or verified |
check free space and the message |
invalid_retention |
a --keep value that is not a positive count |
the newest backup is never pruned |
not_found |
no such id |
usually a stale link |
Generic
| code |
cause |
invalid_request |
the request body failed validation. Field names only — never the values, which end up in logs and screenshots |
http_error |
a plain HTTP-level refusal, with its status |
internal_error |
an unhandled error. The code is all you get; the traceback is in the server log, because it can carry paths and credentials |
Diagnostics warnings
Not errors — reported by diagnostics and worth acting on. They are listed with
their meanings in Diagnostics: disk_low, disk_critical,
cache_over_quota, wal_growth, tool_version_drift, legacy_process_active, and
no_roots.
Startup refusals
Exit codes 2 to 5 are covered by the installation manual under
when it refuses to start.