{"record":{"id":"7566af2cd32fe66c","repo":"Kareadita/Kavita","slug":"kavita-has-already-tried-to-fetch-from-sanitizedb","errorCode":null,"errorMessage":"Kavita has already tried to fetch from {sanitizedBaseUrl} and failed. Skipping duplicate check","messagePattern":"Kavita has already tried to fetch from (.+?) and failed\\. Skipping duplicate check","errorType":"exception","errorClass":"KavitaException","httpStatus":null,"severity":"warning","filePath":"Kavita.Services/Metadata/CoverDbService.cs","lineNumber":114,"sourceCode":"    /// falls back to an internal fallback method if needed. Valid results are saved to disk.\n    /// </remarks>\n    public async Task<string> DownloadFaviconAsync(string url, EncodeFormat encodeFormat, CancellationToken ct = default)\n    {\n        await _urlValidationService.ValidateUrlAsync(url);\n\n        // Parse the URL to get the domain (including subdomain)\n        var uri = new Uri(url);\n        var domain = uri.Host.Replace(Environment.NewLine, string.Empty);\n        var baseUrl = uri.Scheme + \"://\" + uri.Host;\n\n\n        var provider = _cacheFactory.GetCachingProvider(EasyCacheProfiles.Favicon);\n        var res = await provider.GetAsync<string>(baseUrl, ct);\n        if (res.HasValue)\n        {\n            var sanitizedBaseUrl = baseUrl.Sanitize();\n            _logger.LogTrace(\"Kavita has already tried to fetch from {BaseUrl} and failed. Skipping duplicate check\", sanitizedBaseUrl);\n            throw new KavitaException($\"Kavita has already tried to fetch from {sanitizedBaseUrl} and failed. Skipping duplicate check\");\n        }\n\n        await provider.SetAsync(baseUrl, string.Empty, _cacheTime, ct);\n        if (FaviconUrlMapper.TryGetValue(baseUrl, out var value))\n        {\n            url = value;\n        }\n\n        var correctSizeLink = string.Empty;\n\n        try\n        {\n            var htmlContent = await FlurlConfiguration.CreateSafeRequest(url)\n                .GetStringAsync(cancellationToken: ct);\n            var htmlDocument = new HtmlDocument();\n            htmlDocument.LoadHtml(htmlContent);\n\n            var pngLinks = htmlDocument.DocumentNode.Descendants(\"link\")","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/Metadata/CoverDbService.cs#L96-L132","documentation":"Thrown by CoverDbService.DownloadFaviconAsync when the favicon cache (EasyCache Favicon profile) already holds an entry for the baseUrl — meaning a prior attempt for that exact scheme://host failed and was recorded to avoid hammering the remote. The cache acts as a negative-lookup marker: presence == 'we already failed, don't retry until TTL'. The message is logged at Trace and rethrown as a KavitaException.","triggerScenarios":"Two favicon fetches for the same baseUrl within the cache TTL window where the first one failed (network error, no link tags, bad fallback). The second call hits the cached failure marker and throws immediately without re-attempting the network.","commonSituations":"Metadata refresh job and a manual series-detail load both request the same publisher/external-link favicon back-to-back; a transient outage earlier cached a failure and now every retry within TTL short-circuits; misconfigured/blocked external host that consistently fails.","solutions":["Wait for the favicon cache TTL to expire (or clear the EasyCache Favicon profile) then retry.","Fix the underlying fetch failure first — confirm the host is reachable and serves a valid favicon link tag.","If the host is legitimately down, suppress rather than retry to avoid log spam.","As a caller, treat this KavitaException as 'transient, already handled' and degrade gracefully (e.g. show default icon)."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"var cached = await provider.GetAsync<string>(baseUrl, ct);\nif (cached.HasValue) return string.Empty; // already failed recently; degrade silently","typeGuard":null,"tryCatchPattern":"try { favicon = await coverDbService.DownloadFaviconAsync(url, fmt, ct); }\ncatch (KavitaException ex) when (ex.Message.Contains(\"already tried to fetch\"))\n{ /* negative cache hit: use default icon, do not retry within TTL */ }","preventionTips":["Treat the negative-cache message as 'transient, already handled' — don't retry immediately.","To force a retry, clear the EasyCache Favicon profile or wait for TTL expiry.","Fix the underlying network failure so the next window succeeds."],"tags":["favicon","cache","metadata","network","rate-limiting"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}