Skip to content

Troubleshooting

github-actions[bot] edited this page Sep 30, 2026 · 10 revisions

Troubleshooting — Tippani error codes

Every handled error Tippani logs carries a stable code of the form TIP-<SUBSYS>-<NNN> (for example TIP-SRCH-002). When something goes wrong, find the code in docker logs and look it up here.

  • [error] lines and the one-line-per-request access log go to stderr; [warn], [trace] and ordinary progress lines go to stdout. docker logs <container> merges the two, so it shows everything.
  • Lines are tagged: [error] (a failure), [warn] (recovered/degraded, worth knowing), [trace] (deep per-operation detail, only when TIPPANI_LOG_LEVEL=debug).
  • Set TIPPANI_LOG_LEVEL=debug to turn on full per-operation tracing when you are hunting an intermittent problem; leave it unset for normal, quiet operation.
  • An admin can read the same lines without a shell, in Settings → Jobs, on the System logs card: kept for 30 days, filtered by level, time and keyword (a code typed into the search bar finds its lines), and exported as Markdown to hand to somebody. Each job's own log, every outward call it made included, is on the job in Past jobs. Neither keeps a key or a password: a request's line keeps only its query's names, and an outward call's hides every credential in its address. The terminal's lines are unchanged.

The codes are defined in internal/olog/codes.go; a build-time test keeps this document and that registry in lockstep.

The container will not start at all

There is no code for this one, because it happens before the logging subsystem and the HTTP server exist. The container exits 1 and the restart policy loops it, and docker logs holds two lines and nothing else.

Line Cause What to do
open db: … unable to open database file followed by … is owned by uid 0, but tippani runs as uid 65532 … The image runs as the non-root user 65532, and the data directory is not writable by it. Almost always a Docker bind mount whose host directory did not exist when the container first started: Docker creates a missing bind source as root. Run the chown -R 65532:65532 <path> the second line prints, against the path on the host. A named volume is chowned by the image and never needs this.
create data dir: … permission denied Same cause, one level up: the parent of the data directory is not writable either. Same fix, applied to the parent.
migrate: … The database opened but a schema migration failed. Not a permissions problem. Read the migration error. Restore the backup taken before the upgrade; migrations are forward-only, so an older image cannot open a newer database.

Two things that look like this and are not: a healthcheck failure does not restart the container (only an unhealthy status), and a bad TIPPANI_LOG_LEVEL is ignored rather than fatal — including the quotes in - TIPPANI_LOG_LEVEL="debug", which Compose keeps in list form, leaving tracing silently off.

HTTP

Code Meaning Likely cause What to do
TIP-HTTP-000 Unclassified internal server error (the generic 500 fallback). A database, transaction, or encoding failure that has not yet been given a specific code. Read the full [error] line — it includes the request method, path, and underlying cause. If it recurs, the handler should be given a specific code.
TIP-HTTP-001 The TLS certificate/key pair changed on disk but failed to re-load; the previous pair is still served. A renewal wrote a malformed file, or wrote the cert and key non-atomically (Tippani retries once the second file lands). Check TIPPANI_TLS_CERT/TIPPANI_TLS_KEY point at a matching PEM pair. HTTPS keeps working on the old certificate until it expires, so fix the files and the next handshake picks them up — no restart needed.
TIP-HTTP-002 An API request waited ten seconds for a database connection, none came free, and it was answered 503 without changing anything. Every connection in the pool is held and not coming back: the state issue #40 describes. The line ends with the requests in flight, oldest first. If it is a single line during a restore, a reset or a large import, it was that. If it repeats, save the log and the goroutine dump described under HEALTH below, then restart Tippani.
TIP-HTTP-003 A request was still running after the server's 60-second write deadline. Each is named once, with its request id. Expected for a backup download, a restore, an in-app update and a very large import approval, which run as long as they need. On any other route it is a request stuck waiting. Find the same req rN on its access line once it finishes, or on a TIP-HEALTH-001 line if the pool is held. A stuck ordinary request is worth a report with the log.
TIP-HTTP-004 A request's handler panicked. The server recovered, closed that one connection and went on; the line carries the stack. A bug in Tippani. The request is still in the log as a 500, with its access line. Nothing to fix on your side. Please report the full [error] line, with the access line just before it.

HEALTH — the container's health check

/healthz, which the image's HEALTHCHECK calls every 30 seconds, answers 200 when a request arriving now could reach the database and get an answer, and 503 with the reason when it could not. A failed check writes one of these lines, ending with the requests in flight; the first check that passes afterwards writes [health] healthy again after N failed check(s). Docker does not restart a container for being unhealthy; only an orchestrator or an autoheal container does.

Code Meaning Likely cause What to do
TIP-HEALTH-001 The health check could not get a database connection within two seconds. Three failed checks 30 seconds apart mark the container unhealthy. Every pooled connection is held. The line names the requests in flight; the oldest is usually the one holding things up. A single line, or a few, while a large import approval, a restore or a backup runs is a busy pool: a writer can hold a connection for five seconds, longer than this check's two. If it is one line followed by [health] healthy again, it was a burst and needs nothing. If it repeats and docker ps says unhealthy: save docker logs tippani > tippani.log 2>&1 and docker inspect --format '{{json .State.Health}}' tippani > health.json. Then docker kill --signal=QUIT tippani: Tippani does not catch SIGQUIT, so Go writes every goroutine's stack to the log and exits. Save that with docker logs tippani > goroutines.log 2>&1 before anything recreates the container, then docker start tippani, because a container stopped by docker kill counts as stopped by hand and its restart policy does not bring it back. Attach all three to a report.
TIP-HEALTH-002 The health check got a connection, but the database did not answer its read. The database is closed: for a moment during a restore or a reset (a single line followed by "healthy again" is that), or for good if a reopen failed. A single one during a restore or reset needs nothing. If it persists, read the log above it for the restore or reset that failed, and restart.

Healthy, and still nothing loads

What you see What it means
The health check is green, and there is no TIP-HTTP-003 line and no access line for the page you loaded. Your requests are not reaching Tippani. Loading a page always writes an access line, even when the database is stuck, because the page itself needs no database. Compare docker exec tippani /tippani healthcheck (inside the container) with a request from the host; if only the host fails, the problem is the port publish or the network in front of it.
The screen stays empty for about twenty seconds, then shows the sign-in form although you are signed in; signing in answers "changed nothing". The API is refusing requests because no database connection is free. Look for TIP-HTTP-002, then TIP-HTTP-003.
The page's frame loads and the screen stays empty for good. An API call got past the door and is waiting inside a handler. It is named by TIP-HTTP-003 once it passes a minute.
The health check reports Client.Timeout exceeded with no code. The server did not answer within three seconds at all, which points at the storage or the host rather than at the pool.

The image has no shell, wget or kill, so these are all run from the host.

UPDATE — in-app self-update

Code Meaning Likely cause What to do
TIP-UPDATE-001 A Docker Engine API call failed during self-update (identify self, pull the image, or launch the recreater). The socket/proxy lacks a needed permission (a socket proxy must allow CONTAINERS=1, IMAGES=1, POST=1), the registry was unreachable, or the container name could not be resolved. Read the [error] line for the Engine's status and message. For a socket proxy, verify the permission env vars on the proxy container; for the raw socket, verify the mount and the group_add gid. A message reading inspect self: docker 404 for <a> and <b> names the two references it looked for — its own id from /proc and its hostname — and means the daemon knows neither, which normally means this process is not in a container that daemon can see. The guided manual command in Settings always works meanwhile.

The update runs, the version does not change

Not an error code, because nothing here fails: the log shows APPLY requested…, then recreater launched…, the image pulls, and the container goes on running the build it started on. Settings reports the restart happened without the version moving.

The recreater is a one-shot Watchtower, launched detached and AutoRemove, so Tippani never sees its output — if it dies, the app has nothing to report. Before 3.x the default helper was containrrr/watchtower, whose last release (1.7.1, 2023) negotiates Docker Engine API 1.25. A current daemon refuses it —

client version 1.25 is too old. Minimum supported API version is 1.40

— and the helper then panics and exits without touching anything.

Newer builds default to nickfedor/watchtower, the maintained continuation, which negotiates the daemon's own API version. If you are on an older Tippani, or you pinned the helper yourself, set it explicitly:

environment:
  TIPPANI_UPDATER_IMAGE: nickfedor/watchtower

To confirm what the helper is doing, run it in the foreground once — the same command Tippani runs, minus the detach:

docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  nickfedor/watchtower --run-once --cleanup <your container name>

STORE — database lifecycle, integrity, repair

Code Meaning Likely cause What to do
TIP-STORE-001 SQLite PRAGMA quick_check could not run. The database file is unreadable or the handle is broken. Check the data volume is mounted and readable. Restart to re-run startup repair; if it persists, restore from backup.
TIP-STORE-002 quick_check found page-level corruption. An unclean shutdown or an unreliable storage volume tore the file. Startup attempts automatic recovery. Ensure the container stops gracefully (see below) and the volume honours fsync. If recovery fails, use Profile → Rebuild search index, then restore from backup as a last resort.
TIP-STORE-003 A full-text index could not be reconstructed in place. The index shadow tables are too corrupt to drop and recreate. Recovery escalates to a whole-database rebuild automatically. If that also fails (TIP-STORE-004), restore from backup.
TIP-STORE-004 Whole-database recovery (rebuild from intact content) failed. Corruption has spread beyond the search indexes into base tables. Restore from backup. Search stays unavailable until Profile → Reset all data or a restore.
TIP-STORE-005 WAL checkpoint on shutdown failed, or could not finish. A reader still held the database as the server stopped, or another connection was still writing two seconds into the checkpoint (a stuck request, a job shutdown gave up on, a sqlite3 shell); the line then says how many of the WAL's frames were folded back anyway. Harmless — the WAL is valid and replays on the next start. If frequent, check for a stuck long-running request at shutdown.
TIP-STORE-006 Factory reset could not delete a database file. The OS still held the file handle (common briefly on Windows). The server reopens the existing database and reports the error; retry the reset.
TIP-STORE-007 A one-time upgrade pass failed and was skipped. A pass is the third kind of database change — once per database, on an instance that already existed, because a release changed what something means (internal/store/onetime.go). It runs from Migrate(), where a returned error would stop the app from starting, so a failure is logged instead. The commonest cause is the pass's own query meeting data it did not expect. The app boots normally and the pass is left unrecorded, so the next start tries it again — which means this line repeats on every boot until it succeeds or the pass is removed. What is lost is whatever that pass does; for the 2.2.0 pass that is one advisory notice in Settings → Metadata sources, and nothing else.

SRCH — full-text search

Code Meaning Likely cause What to do
TIP-SRCH-001 A full-text search query failed at runtime. The FTS index is corrupt or has drifted from its content table. Tippani reconstructs the index and retries automatically within the same request. No action needed unless it is followed by TIP-SRCH-002.
TIP-SRCH-002 A corrupt FTS index could not be reconstructed while serving a search. Page-level corruption too severe for an in-place rebuild. Restart the server (startup repair escalates to a full recovery) or run Profile → Rebuild search index. Library data is never affected.
TIP-SRCH-003 A search result row failed to scan and was dropped. A SELECT and its target struct drifted apart (usually a migration added a column). Report it — the search query and its scan target need realigning.
TIP-SRCH-004 A fuzzy-search vocabulary read failed; typo correction was skipped. A corrupt FTS index that also failed its one-shot repair, or the 0016 fts5vocab migration did not apply. Run Profile → Rebuild search index or restart. Exact search still works; only the zero-hit typo-correction pass was skipped. If it persists, check for TIP-SRCH-002.

LOCALE — the languages in data/Locales

Code Meaning Likely cause What to do
TIP-LOCALE-001 Two files in data/Locales resolve to the same language code; only one is loaded. The language code is the file name with the extension removed, lower-cased — so FR.txt and fr.txt are both fr, as are fr .txt and fr.TXT. Only possible on a case-sensitive filesystem (Linux); Windows and macOS refuse the second file themselves. The log line names every file that collided and which one won — the exact lower-case spelling is always preferred, so fr.txt beats FR.txt. Rename or delete the others. Nothing is lost: the losing files are read past, not touched.

List scanning — dropped rows

These all mean one row of a list/collection failed to scan and was skipped, so a list came back mysteriously short (the class of bug the 0.6.4 favourites fix exposed). The cause is almost always a SELECT that drifted from the Go struct it scans into — usually a migration that added or reordered a column without updating the query. The fix is to realign the query and the scan target; the log line names the subsystem and the underlying error.

Code Subsystem
TIP-ANNO-001 Annotations list
TIP-DLG-001 Dialogues list
TIP-UTT-001 Quotes list (the ones with no book or film)
TIP-BOOK-001 Books list / count / tags
TIP-MOVIE-001 Movies list / count
TIP-PEOPLE-001 People list / names / orphan images
TIP-REVIEW-001 Quiz / practice candidate rows
TIP-REVIEW-002 An answer's row in the recall history (item_recalls). The grade itself was saved — the log sits beside the schedule rather than inside it, so the card is scheduled correctly and the only loss is one row the review popup can draw. Nothing to do unless it repeats.
TIP-REVIEW-003 The photographs for the credits on Settings → Review → Never asked about. The list itself is unaffected: every chip draws its stand-in instead of a face, which is also what a credit nobody has fetched a person for looks like. Nothing to do unless it repeats.
TIP-EXPORT-001 Export rows
TIP-BULK-001 Bulk-selection id rows
TIP-TAG-001 Genres / tags list
TIP-BOARD-001 Quote boards list
TIP-ANTH-001 Anthologies list / an anthology's entries
TIP-STATS-001 Stats aggregate rows
TIP-STICKER-001 Stickers list
TIP-TRASH-005 The bin list
TIP-ADMIN-001 Admin user list
TIP-META-001 Metadata console / library rows
TIP-IMPORT-002 Import staging queue rows (batches / works / quotes)

IMPORT — the staging queue

A bulk import parses into a holding area and nothing enters the library until it is approved, so these failures leave the library untouched by construction: a failed stage means the file was not taken in, and a failed approve means the work it failed on, and every work after it, is still queued. Nothing is half-applied: a staging is one transaction per file, and an approval one per work — since 3.1.0, when an approval became a queued job that a Stop can land in, so the works it approved before a failure stay in the library and its log in Settings › Jobs names them.

Code Meaning Likely cause What to do
TIP-IMPORT-001 A parsed import could not be written into the staging tables, or an upload could not be kept (or read back) for its import's job; nothing was staged. A database write failed mid-batch, or the data directory's .jobs-spool could not be written or read (disk full, permissions, or corruption — check for TIP-STORE-002). Retry the upload. Nothing is wrong with the file; the library and the existing queue are unchanged.
TIP-IMPORT-003 Approving staged quotes failed; the work it failed on rolled back. A write failed while resolving the destination work or inserting quotes — often a book or film that was deleted while its quotes sat staged. That work's quotes, and every work's after it, are still in the queue (the works before it were approved, and the approval's log in Settings › Jobs names them), so retry the approval. If it recurs, approve one batch (or one work) at a time to find the row that fails.
TIP-IMPORT-004 A staging-queue mutation (bulk edit, retarget or discard) failed; the queue is unchanged. A write failed, or a retarget named a book/film that was deleted mid-edit. Reload the queue so it reflects the current library, then retry. Nothing was partially applied.
TIP-IMPORT-005 No signature matched and no parser claimed the upload; nothing was staged. The file is text, but it is not one of the eight formats the drop target reads — a hand-written note, a CSV, or a saved page the site has since restructured. The row offers Read this as…: pick the format it came from and it is parsed again. If the right format is already picked and it still fails, the page was probably saved as “complete” rather than “HTML only”.

Metadata, covers, people, imports

Code Meaning Likely cause What to do
TIP-META-002 A metadata provider key or setting could not be read. A settings read failed, so a lookup degrades to "unconfigured". Check the database is healthy; re-save the key in Settings → metadata keys.
TIP-META-010 Genres failed to persist even though the request returned OK. The genre write transaction failed to begin or commit. Retry the edit. If it recurs, check for database corruption (TIP-STORE-002).
TIP-COVER-001 An on-demand image download failed: a cover or poster refetch, a role's picture, or a person's headshot. The provider was unreachable or returned no image. The line names the address it asked for. Retry later; a picture uploaded by hand still works. A headshot's identity and facts are kept without it.
TIP-BOOK-002 A book cover could not be fetched on create; the book was saved without one. The cover URL was unreachable or blocked by the SSRF guard. Add a cover later via the cover picker; the book itself saved fine.
TIP-MOVIE-002 A movie poster could not be fetched on create/update; saved without one. The poster URL was unreachable or blocked. Add a poster later; the movie saved fine.
TIP-PEOPLE-010 Garbage-collecting orphaned people rows/images failed. A delete query failed after the parent row was removed. Harmless to users; orphaned rows/images may accumulate. Investigate if disk grows.
TIP-STICKER-002 A starter sticker could not be copied into a user's library; the rest of the set still went in. The MediaCover directory is not writable, or the disk is full. Fix the volume permissions / free disk space. Uploading your own stickers uses the same directory, so this affects that too. The starter set is offered once per instance and is not retried — upload the shapes you want, or restore from a backup taken before the upgrade.
TIP-BOOK-003 A user-supplied cover URL failed to fetch while editing a book; the save was rejected. The URL doesn't point directly at an image, the host blocked/hotlink-protected the request, it exceeded 10 MB, or the fetch timed out/was refused (private-IP guard). Read the full [error] line for the underlying cause; try a direct image link or upload the file instead.
TIP-MOVIE-003 A user-supplied poster URL failed to fetch while editing a movie; the save was rejected. Same causes as TIP-BOOK-003. Read the full [error] line for the underlying cause; try a direct image link or upload the file instead.
TIP-PEOPLE-002 A user-supplied person image URL failed to fetch; the save was rejected. Same causes as TIP-BOOK-003. Read the full [error] line for the underlying cause; try a direct image link or upload the file instead.
TIP-META-011 A provider lookup failed while previewing a re-verify; the item reported fetch_failed. The source (Google Books / Open Library / Amazon / TMDB / TheTVDB) was unreachable, rejected the key, or is out of quota. Retry later; check the key/quota in Metadata → Sources. The rest of the batch still previewed.
TIP-META-012 Writing an approved re-verify change failed for one item. A database write failed mid-apply (or an id collided with another item's). Read the full [error] line; retry the apply for that item. Other items in the batch were unaffected.
TIP-META-013 An approved cover/poster/portrait failed to download on re-verify apply. The image URL was unreachable or blocked by the host allowlist. The item's text fields were still applied; re-run the re-verify or set the image manually.
TIP-META-015 A fetched field could not be encoded during a fill the gaps run over a selection, so that one field was skipped. The provider returned a value of a shape the writer does not accept — effectively a provider or parser change, not a configuration problem. The item's other missing fields were still filled. Re-run fill the gaps, or set the one field by hand; the log line names it.
TIP-META-014 An on-demand book or movie lookup failed at the provider; the client saw a generic 502 ("lookup failed"). Google Books / Open Library / TMDB / TheTVDB was unreachable, rejected the key, or is out of quota — the real cause is on the same [error] line. Read the full [error] line: a 401 / "rejected the key" means fix the key in Metadata → Sources; a 429/quota means retry later or add your own key; a connection error means the container can't reach the provider (check egress/DNS).
TIP-PEOPLE-003 An on-demand person link/portrait lookup failed at the provider; the client saw a generic 502 ("lookup failed"). For authors, Open Library was unreachable or errored; for actors/directors, TMDB was unreachable or rejected the key (including the built-in fallback key hitting its rate cap). The real cause is on the same [error] line. Read the full [error] line: a TMDB 401 means fix/replace the key in Metadata → Sources; a 429 means retry later; a connection error means the container can't reach the provider.
TIP-META-016 A speaker remap rewrote a film line's character but deliberately left its actor alone, because the line names a different number of characters than actors. An ensemble line — "Sam, Frodo" spoken against one actor, or three actors against two characters — has no slot-for-slot pairing to follow. Nothing is broken and nothing was guessed. The character is remapped; the actor field is left exactly as it was, because inventing a pairing would put the wrong actor against the wrong character invisibly and it would read as your own typing. Set the actor by hand on that line if it needs one.
TIP-META-017 An IGDB game lookup failed; the client saw a generic 502, or a 503 when no key is set at all. IGDB needs BOTH a Twitch client id and a client secret, and Twitch answers a wrong one with 400 invalid client rather than a 401 — so "rejected the credentials" usually means one of the two is mistyped or the app was deleted. Otherwise: out of quota (4 req/s), or the container cannot reach api.igdb.com / id.twitch.tv. Check both values in Metadata → Sources; they are a pair and a valid id with a stale secret fails the same way as a wrong id. Register an application at dev.twitch.tv to get a fresh pair. A connection error means egress or DNS, not the key.
TIP-META-018 No Wikidata item claims a game's IGDB slug, so its voice cast could not be fetched. The game itself was saved. Expected for most games and not a fault. Wikidata is the only free structured source of game voice credits and its coverage is thin — of 24 well-known titles measured, 10 had a usable cast and The Witcher 3, Mass Effect 3, Persona 5, Disco Elysium and BioShock had none at all. Nothing to fix. The cast is left blank and is hand-editable: type the credits into the game's Details, and they are kept exactly as a fetched cast would be.

SHELF — status and the read log

Code Meaning Likely cause What to do
TIP-SHELF-001 A shelf cap was asked for a media_type the shelf does not recognise; the tightest (film) cap of two was used. A media type reached the shelf that shelfCap was never taught — either a new one added without an arm, or a corrupted movies.media_type value. Check the log line for the offending value. If it is a media type the app should support, shelfCap in internal/httpapi/shelf.go needs a decided arm rather than a fallthrough; if it is junk, correct the row. Nothing is lost either way — the cap is only a client-side nudge.

CAST — a work's character-to-actor mapping

Every book, film, show and game owns a list of characters with the actor beside each (the voice actor on a game; a book's list has no second column at all). It is seeded by whichever metadata provider pinned the title and is editable by hand, and the rule that keeps those two apart is that a refetch never overwrites a row you have touched — see the header of migration 0048.

Code Meaning Likely cause What to do
TIP-CAST-001 A cast row failed to scan, so that character was left out of the list. A SELECT that drifted from the Go struct it scans into — usually a migration that added or reordered a column in work_cast without updating the query. Cosmetic per row and reported as a 500 only if the whole read fails. Realign the query and the scan target; the log line names the work and the underlying error.
TIP-CAST-002 A cast row's folded lookup keys could not be rewritten by the boot-time repair; the row kept the keys it had. Almost always the pair UNIQUE: two rows on one work whose names differ only by case or by punctuation (Éowyn and éowyn) become the same row once both are folded, and SQLite refuses the second. This repair runs on every start, so refusing to boot over it would be far worse than skipping it. Nothing breaks. That one row goes on being missed by the quote form's actor auto-fill, exactly as it was before the repair ran. Open the work's cast, delete whichever of the two duplicates is wrong, and the next start folds the survivor.
TIP-CLEANUP-001 A quote could not be read during the Settings cleanup sweep, so it was skipped. A SELECT/struct drift, or a row whose text column holds something the scan could not decode. The sweep reads every quote in one pass, so refusing the whole list over one unreadable row would hide every other finding. Nothing is changed by this sweep at all — it only lists. That one quote is absent from the list; every other quote is still reported, and the count of quotes scanned in the reply tells you one went missing.
TIP-CLEANUP-002 An accepted correction on the Stray marks page could not be written. The UPDATE failed for a reason other than a duplicate — a duplicate is counted and reported to the page rather than logged. Disk, a lock held past the busy timeout, or schema drift on the field being rewritten. The whole batch rolls back, so every quote still holds exactly the words it had. This is the only path in the feature that writes to a quote, and it writes inside one transaction for precisely this reason.
TIP-CLEANUP-003 A finding you ignored was not recorded, or the ignored set could not be read. A read or write against cleanup_ignores (0052) failed — usually disk or a lock. Ignoring again works once the underlying problem is gone; the quote is untouched either way. If it was the READ that failed the page shows nothing at all rather than an unfiltered list, deliberately: an unfiltered list re-offers every finding you have already dismissed.

TRASH — the bin

Deleting a book, film, quote or account writes a JSON snapshot of the whole subtree to the trash table first, then deletes the rows. A failure to write the snapshot REFUSES the delete: nothing is removed, which is the safe direction for a feature whose whole purpose is not losing things.

Code Meaning Likely cause What to do
TIP-TRASH-001 A delete could not be binned, so the delete was refused and nothing was removed. A database write failed (disk full, or corruption — check for TIP-STORE-002). Free disk space and retry. The item is still there; nothing is half-deleted.
TIP-TRASH-002 A binned cover/poster could not be parked, restored or purged. The MediaCover volume is not writable, or the file was already removed by hand. Cosmetic: the row is unaffected. Parking fails towards KEEPING the file, so a stale image may sit in MediaCover/ or MediaCover/trash/; both are collected by a later sweep.
TIP-TRASH-003 A restore failed and rolled back; the bin entry is still there. An id collision (should be impossible — see id_floor), a username already taken when restoring an account, or a write failure. Read the full [error] line. The entry is intact, so retrying is safe.
TIP-TRASH-004 The bin's retention sweep failed; expired entries and their files are still on disk. A database or filesystem error during the sweep. Harmless to data; the sweep runs again on the next start and once a day after. Disk use stays higher than the retention setting implies until it succeeds.

BACKUP — backup & restore

Code Meaning Likely cause What to do
TIP-BOOK-004 A book's own chapter list could not be read, so the chapter number and name fields offered no suggestions. A SELECT over annotations' two chapter columns failed — schema drift, or the database unreadable at that moment. Cosmetic. The fields still accept anything you type, and the highlight saves exactly as before; only the dropdown is empty.
TIP-CAST-003 A requested IMDb cast fetch failed. Network (IMDb unreachable, timed out at the shared 10s client timeout), a page that carries no embedded document, or the write. A 404 from IMDb is reported to the reader as "no title with that id" rather than logged. The work's existing cast is unchanged — the merge runs in one transaction, so nothing partial is stored. Check the id against the page in a browser, then press it again; nothing retries on its own, by design.
TIP-CAST-004 A requested TheTVDB cast re-pull failed. Network (TheTVDB unreachable, timed out at the shared 10s client timeout), a key that has expired or been revoked, or the write. A title with no TheTVDB id is refused as a 409 before anything is fetched, and a missing key as a 503; neither is logged here. The work's existing cast is unchanged — the merge runs in one transaction, so nothing partial is stored. Check the key in Settings → Metadata, then press it again; nothing retries on its own, by design.
TIP-CAST-005 A work page's picture pass could not read the work's cast, or could not store a role's picture or an actor's headshot it had fetched. The database refusing a write (a full disk, a file made read-only) or failing a read. A download that failed is not this code: it is logged where it failed, as TIP-COVER-001, a role's picture and a headshot alike. That picture is missing and the page draws what it draws without one; the rest of the pass went on. Fix what the database's own error names, then open the page again: it asks for whatever is still missing.
TIP-BACKUP-001 The backup's database snapshot (VACUUM INTO) failed; no archive was produced. Disk full, or the live database is corrupt (TIP-STORE-002). Free disk space; run Profile → Rebuild search index or check integrity, then retry.
TIP-BACKUP-002 The backup archive could not be written or promoted into backups/, or a safety copy could not be sealed, or its download was cut short. Disk full or a permissions problem on the data volume; for a download cut short, the browser's connection dropped. Free disk space / fix volume permissions and retry. A safety copy cut short is taken again from the restore or reset prompt: its download works once.
TIP-BACKUP-003 Restore could not extract the backup archive to staging. Disk full (restore needs roughly the archive's expanded size free) or a truncated archive. Free disk space; re-create the backup if the archive is damaged.
TIP-BACKUP-004 The restore swap failed; the previous data was rolled back intact. A file in the data dir was locked, or the restored database failed to open/migrate. Read the full [error] line; nothing was lost — retry after fixing the cause.
TIP-BACKUP-005 The restore rollback failed; the server exited so Docker restarts it cleanly. Cascading I/O failure during rollback. The container comes back on whatever is on disk; the previous data dir is preserved in .pre-restore-<ts> inside the data volume for manual recovery.
TIP-BACKUP-006 Backup/restore temporary files could not be cleaned up, or a safety copy's file could not be removed after its download or once it had waited too long. A lingering file lock (Windows) or permissions. Harmless to data; delete stray .backup-* / .restore-* dirs, .safety-*.tpbk files and old backups/*.partial files to reclaim disk. A restart removes every .safety-* file by itself.
TIP-BACKUP-007 An uploaded restore archive could not be spooled to disk. Disk full (the upload needs the archive's size free on the data volume) or a permissions problem. Free disk space / fix volume permissions and retry the upload; live data is untouched.
TIP-BACKUP-008 The backup could not leave the job history and system log out of its database snapshot; no archive was produced. Disk full (stripping rewrites the snapshot once more), or the snapshot could not be written in the backup's staging directory. Free disk space on the data volume and retry; live data is untouched.
TIP-BACKUP-009 A restore could not carry the job history and system log over from the database it replaced. The restore went ahead; the restored server starts with no history. The replaced database in .pre-restore-<ts> could not be read (removed by hand, or damaged). Nothing to fix for the library, which restored. The old history is still in .pre-restore-<ts>/tippani.db until the next restore.
TIP-BACKUP-010 An uploaded restore archive stopped arriving: nothing came for a minute, so the upload was given up. Nothing was changed. While an admin's upload waited, no job could start; they can again. The browser's connection dropped mid-upload (a laptop asleep, a phone that lost its signal), or a reverse proxy in front of Tippani stopped passing the body on. Upload again on a steadier connection. Behind a proxy, check that it streams request bodies and does not time them out.
TIP-AUTH-001 A single sign-on could not start, or the provider's answer failed validation. TIPPANI_OIDC_ISSUER unreachable from the server, an issuer that does not match its own discovery document (a trailing path or http/https mismatch), a redirect URL the provider does not have registered, or a clock skewed by more than a minute. The full [warn] line names which check failed. Fix the provider or the TIPPANI_OIDC_* settings and press the sign-in button again; password sign-in is unaffected.
TIP-NOTIFY-001 A Pushover message was not accepted. Network, TIPPANI_OFFLINE switched on, or an invalid user key / application token (Pushover answers 4xx). Check the keys in Profile → Notifications and press Send a test; the import, fetch or backup that triggered the message completed regardless.

LOG and JOBS — the logs and jobs kept in the database

The database keeps a copy of the system log and every job's log for 30 days, read in Settings → Jobs: Current jobs and Past jobs hold each job with its log and an Export to Markdown (a reader sees their own jobs, an admin everybody's), and the System logs card, an admin's alone, holds the system log, filtered by level, time and keyword. The same reads answer a script, or a browser signed in to Tippani: /api/jobs?view=past lists the jobs that have ended, newest first (view=current: the ones waiting or running), /api/jobs/<id> is one job with its log and /api/jobs/<id>/log.md that log as Markdown; /api/admin/logs (narrowed with level, q and a window: since, milliseconds back from the server's own now, or from and to in unix milliseconds, and upto, the newest line id to include; each answer names the window it read as from and upto) and /api/admin/logs.md (the same filters, or ?all=1 for everything kept) are the system log. stdout and stderr are unaffected: docker logs still has every line, including the ones a code below says the database did not keep.

Code Meaning Likely cause What to do
TIP-LOG-001 The log writer stopped on an internal error and restarted; the lines it was writing were not kept in the database. A bug in Tippani. The [error] line carries the stack. Nothing to fix on your side, and nothing else is affected. Please report the full [error] line.
TIP-LOG-002 A batch of log lines could not be written to the database; those lines were not kept there. Another writer held the database's write lock through every retry (a long import or restore), the disk is full, or the database is damaged (TIP-STORE-002). Free disk space if it is short. A one-off during a large import is harmless: the lines are still in docker logs.
TIP-LOG-003 Log lines arrived faster than the database could take them, and some were not kept. The system log says how many. A burst of requests (a script, a crawler, many readers at once) on slow storage. Request and file lines are dropped first; job lines and errors last. Nothing, if it is rare. If it is frequent, check the storage the data volume sits on.
TIP-LOG-004 The prune that removes jobs and log lines older than 30 days failed. The database was busy or read-only at the time. Nothing is lost; old lines stay until the next prune succeeds, which is tried again once a log line is written an hour or more after the failed one began, or at once when Settings → Jobs opens (its first read of the job list asks for one, /api/jobs?view=past&prune=1).
TIP-LOG-005 At shutdown, the log lines still waiting to be written when the flush's budget ran out were not kept in the database. The database's write lock was held, or the storage was slow, as the server stopped. Shutdown gives the log one second, or less if the stop has already used most of Docker's ten-second grace, so the whole stop fits inside it. Nothing to fix. The lines are still in docker logs; only the database's copy of the last second, the one Settings → Jobs shows, is missing them.
TIP-JOBS-001 A job stopped on an internal error. It was marked failed, its log kept, and the next job in the queue started. A bug in Tippani, set off by something in the item the job was working on. The [error] line carries the stack. Please report the full [error] line. Whoever started the job can run it again from Settings → Jobs (POST /api/jobs/<id>/rerun); if it fails the same way on the same item, leave that item out.
TIP-JOBS-002 A job's state, progress or result could not be written to the database, so Settings → Jobs (and /api/jobs/<id>) may show it out of date. A job's start and end are retried for about half a minute first; when its end still could not be written, the line says so and names how the job actually ended. Or, when the line says the database was not swapped: a restore or a factory reset was refused before it touched anything, or a search rebuild's recovery of the whole database was, once the indexes that could be rebuilt in place had been (the rebuild then lists the rest as failed), because a waiting job somebody had stopped could not first be written stopped in the database it was about to replace. Another writer held the database's write lock through every retry (a long import, a sqlite3 shell, slow storage), the disk is full, or the database is damaged (TIP-STORE-002). Free disk space if it is short. A job shown as running after its work ended stays that way until Stop (or Stop all) is pressed on it in Settings → Jobs (POST /api/jobs/<id>/stop, POST /api/jobs/stop-all), which marks it interrupted at once, or until the next restart does; either way whoever started it can run it again. A refused restore, reset or rebuild left the library as it was: close whatever else has the database open, then press it again.
TIP-JOBS-003 Settings → Jobs could not read the job history, a job's log or the system log (/api/jobs…, /api/admin/logs…), and answered "internal error"; or a Markdown export stopped before its end. The database was busy or damaged (TIP-STORE-002) while the list or the log was read. An export also stops early when the download is given up: the browser closed it, or it took nothing for a minute (a phone asleep mid-download). And a restore or a factory reset that replaces the database part-way through an export ends it, with a last line inside the file saying so. Reload Settings → Jobs. If it happens every time, look for TIP-STORE-002 in docker logs and follow that row. An export that stopped early can be downloaded again.

Clone this wiki locally