{"record":{"id":"86f3f8ba2b37f9a4","repo":"Radarr/Radarr","slug":"simkl-api-call-resulted-in-an-unexpected-statuscod","errorCode":null,"errorMessage":"Simkl API call resulted in an unexpected StatusCode [{0}]","messagePattern":"Simkl API call resulted in an unexpected StatusCode \\[(.+?)\\]","errorType":"exception","errorClass":"ImportListException","httpStatus":null,"severity":"error","filePath":"src/NzbDrone.Core/ImportLists/Simkl/SimklParser.cs","lineNumber":76,"sourceCode":"                foreach (var movie in jsonResponse.Movies)\n                {\n                    movies.AddIfNotNull(new ImportListMovie\n                    {\n                        Title = movie.Movie.Title,\n                        TmdbId = int.TryParse(movie.Movie.Ids.Tmdb, out var tmdbId) ? tmdbId : 0,\n                        ImdbId = movie.Movie.Ids.Imdb,\n                    });\n                }\n            }\n\n            return movies;\n        }\n\n        protected virtual bool PreProcess(ImportListResponse netImportResponse)\n        {\n            if (netImportResponse.HttpResponse.StatusCode != HttpStatusCode.OK)\n            {\n                throw new ImportListException(netImportResponse, \"Simkl API call resulted in an unexpected StatusCode [{0}]\", netImportResponse.HttpResponse.StatusCode);\n            }\n\n            if (netImportResponse.HttpResponse.Headers.ContentType != null && netImportResponse.HttpResponse.Headers.ContentType.Contains(\"text/json\") &&\n                netImportResponse.HttpRequest.Headers.Accept != null && !netImportResponse.HttpRequest.Headers.Accept.Contains(\"text/json\"))\n            {\n                throw new ImportListException(netImportResponse, \"Simkl API responded with html content. Site is likely blocked or unavailable.\");\n            }\n\n            return true;\n        }\n    }\n}\n","sourceCodeStart":58,"sourceCodeEnd":89,"githubUrl":"https://github.com/Radarr/Radarr/blob/ca451608dc60c6cec754aba8d96bfa30e9468ed5/src/NzbDrone.Core/ImportLists/Simkl/SimklParser.cs#L58-L89","documentation":"SimklParser.PreProcess rejects the Simkl API HTTP response when its status code is not 200 OK. It is a hard precondition gate: if the upstream Simkl endpoint returned any non-OK status (4xx/5xx/redirect), parsing is skipped entirely and an ImportListException is thrown so the import run fails fast instead of deserializing a likely-empty or error body.","triggerScenarios":"A Simkl import list fetch returns a non-200 status: expired/invalid OAuth token (401), bad request (400), rate limiting (429), Simkl maintenance/5xx, or a network proxy returning a 502. The status code is interpolated into the message.","commonSituations":"Simkl auth token expired or revoked; Simkl user/username setting wrong causing 4xx; transient Simkl outage; corporate proxy rewriting responses; the Simkl API base URL was overridden to a wrong endpoint.","solutions":["Re-authenticate the Simkl account / refresh the token in the import list settings and retry.","Open the exact Simkl endpoint URL in a browser to see the real status/body (401 vs 429 vs 500 narrows the cause).","If 429, wait and reduce the import sync interval; Simkl is rate-limiting the requests.","Verify the configured Simkl username/list type is valid for the account.","Check network egress (proxy/DNS) can reach api.simkl.com without an intermediary returning a non-OK status."],"exampleFix":"// before: hard failure on any non-OK status\nif (netImportResponse.HttpResponse.StatusCode != HttpStatusCode.OK)\n    throw new ImportListException(...);\n\n// after: tolerate transient 5xx/429 and surface auth errors distinctly\nvar sc = netImportResponse.HttpResponse.StatusCode;\nif (sc == HttpStatusCode.Unauthorized || sc == HttpStatusCode.Forbidden)\n    throw new ImportListException(netImportResponse, $\"Simkl auth failed ({sc}). Re-link the account.\");\nif ((int)sc is 429 or >= 500)\n    throw new RequestLimitReachedException($\"Simkl unavailable/rate-limited ({sc}). Retry later.\");\nif (sc != HttpStatusCode.OK)\n    throw new ImportListException(netImportResponse, $\"Simkl unexpected status {sc}\");","handlingStrategy":"try-catch","validationCode":"// Validate Simkl credentials and reachability before triggering an import run.\nif (string.IsNullOrWhiteSpace(settings.BaseUrl)) return ConfigError(\"BaseUrl missing\");\nif (string.IsNullOrWhiteSpace(settings.AccessToken)) return ConfigError(\"Simkl token missing\");\n// optional liveness probe:\n//   var probe = await httpClient.GetAsync($\"{settings.BaseUrl}/users/settings\");\n//   if (!probe.IsSuccessStatusCode) return ConfigError($\"Simkl probe status {probe.StatusCode}\");","typeGuard":"// Determine whether a Simkl response is safe to parse (status OK and not the html-block case).\nbool IsParseableSimklResponse(ImportListResponse r)\n    => r.HttpResponse.StatusCode == HttpStatusCode.OK\n       && !(r.HttpResponse.Headers.ContentType?.Contains(\"text/json\") == true\n            && r.HttpRequest.Headers.Accept?.Contains(\"text/json\") != true);","tryCatchPattern":"try { return _parser.ParseResponse(listResponse); }\ncatch (ImportListException ex) when (ex.Message.Contains(\"Simkl API call\"))\n{\n    var code = listResponse.HttpResponse.StatusCode;\n    if (code == HttpStatusCode.Unauthorized || code == HttpStatusCode.Forbidden)\n        throw new ImportListException(listResponse, \"Simkl auth failed ({0}). Re-link the account.\", code);\n    if ((int)code == 429 || (int)code >= 500)\n        return Array.Empty<ImportListResponse>(); // transient: treat as empty this run\n    throw;\n}","preventionTips":["Keep the Simkl OAuth token fresh; re-link on any 401.","Probe the endpoint after config changes.","Throttle syncs to avoid 429s."],"tags":["simkl","import-list","http","preprocess","network"],"backgroundTag":null,"analyzedSha":"ca451608dc60c6cec754aba8d96bfa30e9468ed5","analyzedAt":"2026-08-13T17:21:54.115Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}