Skip to content

Decision log — why was this subtitle chosen?

When a search picks the “wrong” subtitle — or finds nothing at all — the useful question is not what happened but why. The decision log answers that without digging through log files: every search records its complete selection pipeline, and the result is stored alongside the download.

SituationWhere the log livesHow to open it
A subtitle was downloadedWith the downloadActivity → History → the why? action on the row
Nothing was foundOn the wanted itemWanted → the item’s “Why was nothing found?” action

History rows only show the action when a log actually exists. Downloads made by older builds, before Sublarr started recording this, have none.

The log follows the same order the pipeline runs in.

For every provider Sublarr either queried or skipped:

  • queried — how many candidates came back, and how long it took
  • skipped — with the reason:
ReasonMeaning
circuit_openThe provider’s circuit breaker is open after repeated failures. See Circuit breaker.
rate_limitedThe provider is in a rate-limit cooldown.
budget_exhaustedThe daily API budget for this provider is used up.
no_pool_keyNo usable API key in the pool — all exhausted, cooling after a 429, or none configured.
auto_disabledSublarr disabled the provider automatically after a sustained failure streak.

Every candidate that survived the provider stage runs through a chain of filters. The log records, per stage, how many candidates went in, how many came out, and which ones were dropped:

StageDrops a candidate when
wrong_episodeThe release name states another season or episode than the one searched (S01E12 for S02E12, 1x5 for S01E06). Runs first, for every provider. Absolute numbering of the right episode, season packs of the right season, batches whose range contains the episode, the episode’s own title and hash matches are kept; a result that names no episode at all is kept only when the provider matched the episode itself. The log shows the wanted episode and why each result was dropped.
languageThe subtitle is not in a requested language.
formatThe format is excluded — e.g. a series that requires ASS.
min_scoreThe score is below the profile cutoff or the per-language threshold.
blacklistThe exact subtitle was blacklisted earlier.
forced_onlyThe forced/non-forced flag doesn’t match the request.
release_group_excludeThe release group is on the blocked list.
profile_must_containThe filename lacks a term the profile requires.
profile_must_not_containThe filename contains a term the profile forbids.

Download attempts (including retries and which provider served the file). A downloaded candidate can still be rejected here, and the next one is tried:

OutcomeMeaning
episode_mismatchThe file the provider took out of an archive names another episode (Show.S02E05.zip holding Show.S01E05.srt).
length_mismatchThe subtitle does not fit the video’s length: more than a few cues end past the end of the video plus the larger of 10 % and 2 minutes, or the cues cover less than half of the video. Forced and signs subtitles may cover little of the video. Skipped when the video’s length is unknown.

Then the upgrade decision when an existing subtitle was replaced, and the final pick with its complete score breakdown — every component that contributed points, including release-group bonuses and each penalty-rule hit.

The modal opens on a compact view: which provider won, the final score, and the count of candidates rejected per stage. That is usually enough to answer “why this one”.

Expert mode expands it to the per-candidate detail: every rejected subtitle with its stage and reason, and per-provider timings. This is the view worth copying into a support thread — it is far more useful than a log excerpt, and it contains no credentials.

Recording is on by default and costs a JSON snapshot per search. Disable it with decision_log_enabled (Settings → Subtitles → Automation) if you don’t want the storage.

With the setting off, the why? action disappears for new searches; existing logs are kept.

  • A preview does not record. A dry run deliberately writes nothing at all, including the decision log — it shows its result inline instead.
  • Successful downloads store the log with the download, not the wanted item. The wanted row is removed once it is satisfied, so a not_found, failed or skipped outcome is what leaves a log behind on the item.

The same payload is available programmatically:

GET /api/v1/history/{download_id}/decision
GET /api/v1/wanted/{item_id}/decision

Both return 404 with a plain message when no log was recorded for that entry.