Troubleshooting
Common failure modes and how to diagnose them. If your symptom isn't
here, check the structured logs (logging.json = true makes them
greppable) and the admin metrics tab — most operational questions are
answerable from one of those two surfaces.
Resolver
Many releases land in the review queue
Check the review queue cards themselves. Every card shows the cleaned search query the resolver used and the cleanup rules that fired. Common patterns:
- Cleaner missed a keyword. Look at uploader-specific patterns
(e.g. a new keyword like
Remastered). Add to[ingestion.cleanup.extra_format_keywords]and use the Retry button on the queue card. - Dice score just under threshold. The candidate is right but
noisy title text drags the score. If this happens systematically
for a known-good release shape, lower
resolution_threshold(e.g. from 0.85 to 0.80). Don't drop it below 0.70 — false-positive risk rises sharply. - No candidates at all. The cleaned query produced no FTS hits. Try the Search provider modal manually — if the series is in MangaBaka, the cleaner is being too aggressive; flag it as a bug. If MangaBaka doesn't have it, Reject the release.
A release auto-resolved to the wrong series
Use the Search provider modal to find the correct series, then
click "Link" on the right hit. The release transitions to resolved
with resolution_path = "manual". The wrong link is overwritten.
If a pattern of wrong matches surfaces (same uploader, same shape), file an issue with the cleaned query + the candidate's Dice score — the cleaner rule order may need a tweak.
Provider returned 503 Misconfigured
The auth.admin_token config value is unset. Set it and restart. The
503 is intentional — distinct from 401 Unauthorized so a fresh
deploy doesn't look like a credentialing bug.
Sources
Nyaa source returns zero releases
Check the admin Sources card:
last_error— if populated, the feed URL is wrong or Nyaa is unreachable. Verify withcurl <feed_url>from the host.fetched_count = 0but no error — the feed is responding but empty for the configured filters. Try a less restrictive feed URL in a browser.- Stale
last_polled_at— the scheduler isn't running the source. Verifycronis set and the source isenabled = true. Thetsundoku poll --source <name>one-shot also works for diagnosis.
MangaUpdates legacy ID resolution looks stuck
The mangaupdates_id_map table caches per-legacy-ID translations,
including tombstones for dead IDs. To inspect:
sqlite3 ${data_dir}/db/tsundoku.db \
'SELECT legacy_id, modern_id, resolved_at FROM mangaupdates_id_map ORDER BY resolved_at DESC LIMIT 20'
Rate-limit recovery: the resolver throttles at 1 req/sec per host
with exponential backoff on 429. A long outage tightens the next-
allowed timestamp; failed lookups don't cache, so the next poll
retries naturally.
Providers
Refresh cache button takes a long time
The MangaBaka dump is ~476 MB compressed. First-time refresh fetches, verifies SHA-1, extracts, and adds 8 indexes + an FTS5 mirror. On a modest machine this can take 3–5 minutes. Subsequent refreshes are faster because rotation reuses the extraction path.
If refresh-provider-cache hangs longer than that, kill it and check the
host's network. The download supports resume via HTTP Range; rerun
the refresh and it picks up where it left off.
Cache misses despite a recent refresh
Two scenarios:
- The series is genuinely missing from MangaBaka's dump. Enable
api_fallback = true+api_key = "..."to fall back to the live API for cache misses. - The series exists but under a different foreign ID. Use the Search provider modal to find it manually.
A negative-cache tombstone may also be holding back retries. The TTL
is negative_cache_ttl_days (default 7).
Database
"database is locked" errors
The connection pool is pinned to one writer, so this should be rare. If you see it: another process is holding a write lock. Verify only one tsundoku instance is running against the data dir.
The busy_timeout is 5000 ms — if the offending writer doesn't
release within that window, you'll see the error.
Reset to a clean slate
systemctl stop tsundoku
rm ${data_dir}/db/tsundoku.db
tsundoku migrate
# (optional) tsundoku refresh-provider-cache
systemctl start tsundoku
This drops every release, every series, every review-queue card. The
MangaBaka offline cache survives — that's a separate file at
${data_dir}/cache/providers/mangabaka/series.sqlite. Delete it too
if you want a full reset.
Logging
tracing-subscriber honors RUST_LOG. Examples:
RUST_LOG=tsundoku=debug tsundoku serve
RUST_LOG=tsundoku=trace,sea_orm=warn tsundoku serve
RUST_LOG=tsundoku=debug,td_resolution=trace tsundoku serve
For JSON-structured logs (suitable for Loki / journald aggregators),
set logging.json = true in config.
When to file an issue
github.com/skewb1k/tsundoku/issues.
Useful in the report:
- tsundoku version (
tsundoku --versionor the GHCR tag). - The relevant config block (redact
api_keyandadmin_token). - Logs at
debuglevel around the failing operation. - For resolution bugs: the raw title from Nyaa, the cleaned query the card shows, and a screenshot of the review card if possible.