Jackett/Jackett · error · Exception

Download links not found. Make sure you can download from th

Error message

Download links not found. Make sure you can download from the website.

What it means

Thrown during FunFile.PerformQuery while iterating search-result rows. Each row (matched by the selector 'table.mainframe table[cellpadding="2"] > tbody > tr:has(td.row3)') is expected to contain an anchor with an href starting with 'download.php'. If that anchor is missing on any row, the indexer aborts the entire parse with this message. This is a scraper-fragility error: it fires when the site's HTML markup does not match the indexer's assumptions.

Source

Thrown at src/Jackett.Common/Indexers/Definitions/FunFile.cs:209

            if (results.IsRedirect) // re-login
            {
                await ApplyConfiguration(null);
                results = await RequestWithCookiesAndRetryAsync(searchUrl);
            }

            char[] delimiters = { ',', ' ', '/', ')', '(', '.', ';', '[', ']', '"', '|', ':' };

            try
            {
                var parser = new HtmlParser();
                using var dom = parser.ParseDocument(results.ContentString);

                var rows = dom.QuerySelectorAll("table.mainframe table[cellpadding=\"2\"] > tbody > tr:has(td.row3)");
                foreach (var row in rows)
                {
                    var qDownloadLink = row.QuerySelector("a[href^=\"download.php\"]");
                    if (qDownloadLink == null)
                        throw new Exception("Download links not found. Make sure you can download from the website.");

                    var link = new Uri(SiteLink + qDownloadLink.GetAttribute("href"));

                    var qDetailsLink = row.QuerySelector("a[href^=\"details.php?id=\"]");
                    var title = qDetailsLink?.GetAttribute("title")?.Trim();
                    var details = new Uri(SiteLink + qDetailsLink?.GetAttribute("href")?.Replace("&hit=1", ""));

                    var categoryLink = row.QuerySelector("a[href^=\"browse.php?cat=\"]")?.GetAttribute("href");
                    var cat = ParseUtil.GetArgumentFromQueryString(categoryLink, "cat");

                    var seeders = ParseUtil.CoerceInt(row.Children[9].TextContent);
                    var leechers = ParseUtil.CoerceInt(row.Children[10].TextContent);

                    var release = new ReleaseInfo
                    {
                        Guid = link,
                        Link = link,
                        Details = details,

View on GitHub (pinned to adff194147)

Solutions

  1. Open the FunFile search page in a browser and confirm download links are present on result rows.
  2. Check if the FunFile site was recently redesigned; if so, the indexer selector needs updating.
  3. Ensure the configured account has download permissions for all categories being searched.
  4. Skip rows missing the link instead of throwing, so a single bad row does not kill the whole query.
  5. Report the HTML change to the Jackett maintainers with a sample of the page source.

Example fix

// before
var qDownloadLink = row.QuerySelector("a[href^=\"download.php\"]");
if (qDownloadLink == null)
    throw new Exception("Download links not found. Make sure you can download from the website.");
// after - skip rows without a link
var qDownloadLink = row.QuerySelector("a[href^=\"download.php\"]");
if (qDownloadLink == null)
    continue;
Defensive patterns

Strategy: try-catch

Validate before calling

// Before parsing, confirm results page actually has download rows
var rows = dom.QuerySelectorAll("table.mainframe table[cellpadding=\"2\"] > tbody > tr:has(td.row3)");
if (!rows.Any(r => r.QuerySelector("a[href^=\"download.php\"]") != null))
    logger.Warn("No downloadable rows found — site layout may have changed.");

Try / catch

try
{
    var releases = await indexer.PerformQuery(query);
}
catch (Exception ex) when (ex.Message.Contains("Download links not found"))
{
    // Scraper mismatch — FunFile HTML changed or account lacks download rights
    logger.Error("FunFile parse failure: {Message}", ex.Message);
    // Do not retry blindly; the selector needs updating
}

Prevention

When it happens

Trigger: A matched table row has no download link anchor — because the torrent was deleted, the user lacks download permission for that torrent, or FunFile changed its table/column HTML layout so rows match but links moved or were renamed.

Common situations: FunFile pushes a UI redesign that renames download.php links or wraps them in JavaScript; a low-seed/dead torrent row renders without a download button; the user's account class lacks download rights for certain categories.

Related errors


AI-assisted analysis of Jackett/Jackett@adff194147 (2026-08-13). Data as JSON: /api/errors/c919d34798837c98. Report an issue: GitHub.