Bug: Jackett searches can block until slow or unhealthy indexers complete
@disaac is already working on this.
Since Sep 15, 2026.
- Dominant language
- Python
- Stars
- 1.3k
- Forks
- 83
- PR merge metrics
- No merged PRs in 30d
Description
Bug: Jackett searches can block until slow or unhealthy indexers complete
Summary
Nefarious Jackett searches can intermittently time out because Nefarious uses Jackett’s manual JSON search endpoint, which waits for all matching indexers to complete without a bounded aggregate timeout.
This is especially visible when one or more configured indexers are slow, unhealthy, or depend on FlareSolverr.
Current behavior
Nefarious builds Jackett search requests using the manual/UI JSON endpoint:
GET /api/v2.0/indexers/{filter}/results
The request is constructed by get_jackett_search_url() in src/nefarious/jackett.py.
search.py then parses the JSON response using the Results field from Jackett’s ManualSearchResult response.
For the all filter, Jackett’s manual endpoint aggregates matching indexers with an unbounded Task.WhenAll(tasks). As a result, Nefarious does not receive a response until every queried indexer either completes or fails.
A single slow indexer can therefore delay the entire search long enough for Nefarious, its HTTP client, or an upstream proxy to time out.
Expected behavior
A Jackett search should return successfully available results within a bounded amount of time, rather than waiting indefinitely for every configured indexer.
Slow or failing indexers should not prevent healthy indexers from returning results.
Impact
- Searches can fail or time out despite healthy indexers having available results.
- One problematic indexer can delay all searches.
- Indexers backed by FlareSolverr are particularly likely to increase request duration.
- A FlareSolverr request may wait up to Jackett’s configured
FlareSolverrMaxTimeout, which defaults to 55 seconds. - This can make Nefarious appear unreliable even when most configured indexers are functioning normally.
Proposed fix
Switch Nefarious from Jackett’s manual JSON endpoint to its Torznab endpoint.
Use:
GET /api/v2.0/indexers/{filter}/results/torznab
with standard Torznab parameters, for example:
# General/movie search
/results/torznab?t=search&q=<query>&cat=<category>
# TV search
/results/torznab?t=tvsearch&q=<query>&cat=<category>
This requires replacing the current Category[] usage with Torznab-compatible cat= parameters.
Why Torznab is preferable
For the all filter, Jackett’s Torznab endpoint uses the all meta-indexer. That path:
- Starts matching indexer searches in parallel.
- Uses a bounded aggregate wait of 40 seconds.
- Returns results from indexers that completed successfully during that window.
- Allows unfinished indexer searches to continue server-side in the background.
- Lets late-completing indexers populate Jackett’s cache when they finish.
In contrast, the currently used manual /results endpoint performs an unbounded aggregate wait for all indexer tasks.
Using Torznab would therefore provide Jackett’s existing parallel, bounded-response behavior without implementing new concurrency orchestration in Nefarious.
Required implementation changes
- Update
get_jackett_search_url()insrc/nefarious/jackett.pyto target/results/torznab. - Select the appropriate Torznab query type:
t=searchfor general/movie searches.t=tvsearchfor TV searches.
- Pass category filtering through
cat=rather thanCategory[]. - Update
search.pyto parse the Torznab XML/RSS response instead of the current JSONManualSearchResultpayload. - Preserve Nefarious’s existing result normalization, filtering, deduplication, and error-handling behavior where possible.
- Add tests covering successful XML parsing, empty results, malformed XML, and partial results when one or more indexers are slow or unavailable.
Limitations / possible follow-up work
Using Torznab improves the current behavior, but it does not provide complete control over indexer isolation or timeout policy.
Jackett’s built-in aggregate timeout is currently fixed at 40 seconds. It also does not provide a way for Nefarious to exclude a known-problematic indexer from a specific search while continuing to query the rest.
Potential follow-up improvements could include:
- Configurable Nefarious-side request timeouts.
- Support for selecting or excluding specific Jackett indexers per search.
- Optional separation of FlareSolverr-backed indexers from normal indexer searches.
- A configurable strategy for whether slow indexers should be queried, skipped, or handled in a separate batch.
Research notes
The current endpoint behavior was verified in Jackett’s codebase:
get_jackett_search_url()currently builds requests to/api/v2.0/indexers/{filter}/resultswithout the/torznabsuffix or a Torznabt=parameter.- Jackett’s
ResultsController.Results()action serves the manual/UI JSON search API. - Its JSON structure matches Nefarious’s current parsing logic:
search.pyreadsresponse["Results"]. - The manual endpoint aggregates indexer tasks using
Task.WhenAll(tasks)without an aggregate timeout or cancellation. - Jackett also exposes
/api/v2.0/indexers/{filter}/results/torznab, handled byResultsController.Torznab(). - For the
allfilter, the Torznab path uses theallmeta-indexer, which runs indexers in parallel and races the aggregate against a 40-second timeout. - Timed-out indexer tasks are not cancelled; they continue in the background and may still populate Jackett’s cache after completion.
- FlareSolverr-backed indexer requests are bounded separately by Jackett’s
FlareSolverrMaxTimeout, which defaults to 55,000 ms.
Acceptance criteria
- Nefarious uses Jackett’s
/results/torznabendpoint for Jackett-backed searches. - Movie/general and TV searches use the correct Torznab query type.
- Category filtering is translated to Torznab
cat=parameters. - Torznab XML/RSS responses are parsed into the existing Nefarious result model.
- A slow or failed indexer does not prevent completed results from healthy indexers from being returned within Jackett’s bounded response window.
- Test coverage includes partial results and XML parsing failures.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.