96 lines
5.2 KiB
Markdown
96 lines
5.2 KiB
Markdown
# Errors and refusals
|
|
|
|
[← Documentation index](index.md)
|
|
|
|
Every error the API returns carries a code:
|
|
|
|
```json
|
|
{"error": {"code": "lock_held", "message": "a library_write job is already running"}}
|
|
```
|
|
|
|
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](stages/renames.md). 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](stages/albums.md) |
|
|
| `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](stages/diagnostics.md): `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](installation.md#when-it-refuses-to-start).
|