Radarr/Radarr · error · ImportListException

Simkl API call resulted in an unexpected StatusCode [{0}]

Error message

Simkl API call resulted in an unexpected StatusCode [{0}]

What it means

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.

Source

Thrown at src/NzbDrone.Core/ImportLists/Simkl/SimklParser.cs:76

                foreach (var movie in jsonResponse.Movies)
                {
                    movies.AddIfNotNull(new ImportListMovie
                    {
                        Title = movie.Movie.Title,
                        TmdbId = int.TryParse(movie.Movie.Ids.Tmdb, out var tmdbId) ? tmdbId : 0,
                        ImdbId = movie.Movie.Ids.Imdb,
                    });
                }
            }

            return movies;
        }

        protected virtual bool PreProcess(ImportListResponse netImportResponse)
        {
            if (netImportResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
            {
                throw new ImportListException(netImportResponse, "Simkl API call resulted in an unexpected StatusCode [{0}]", netImportResponse.HttpResponse.StatusCode);
            }

            if (netImportResponse.HttpResponse.Headers.ContentType != null && netImportResponse.HttpResponse.Headers.ContentType.Contains("text/json") &&
                netImportResponse.HttpRequest.Headers.Accept != null && !netImportResponse.HttpRequest.Headers.Accept.Contains("text/json"))
            {
                throw new ImportListException(netImportResponse, "Simkl API responded with html content. Site is likely blocked or unavailable.");
            }

            return true;
        }
    }
}

View on GitHub (pinned to ca451608dc)

Solutions

  1. Re-authenticate the Simkl account / refresh the token in the import list settings and retry.
  2. Open the exact Simkl endpoint URL in a browser to see the real status/body (401 vs 429 vs 500 narrows the cause).
  3. If 429, wait and reduce the import sync interval; Simkl is rate-limiting the requests.
  4. Verify the configured Simkl username/list type is valid for the account.
  5. Check network egress (proxy/DNS) can reach api.simkl.com without an intermediary returning a non-OK status.

Example fix

// before: hard failure on any non-OK status
if (netImportResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
    throw new ImportListException(...);

// after: tolerate transient 5xx/429 and surface auth errors distinctly
var sc = netImportResponse.HttpResponse.StatusCode;
if (sc == HttpStatusCode.Unauthorized || sc == HttpStatusCode.Forbidden)
    throw new ImportListException(netImportResponse, $"Simkl auth failed ({sc}). Re-link the account.");
if ((int)sc is 429 or >= 500)
    throw new RequestLimitReachedException($"Simkl unavailable/rate-limited ({sc}). Retry later.");
if (sc != HttpStatusCode.OK)
    throw new ImportListException(netImportResponse, $"Simkl unexpected status {sc}");
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate Simkl credentials and reachability before triggering an import run.
if (string.IsNullOrWhiteSpace(settings.BaseUrl)) return ConfigError("BaseUrl missing");
if (string.IsNullOrWhiteSpace(settings.AccessToken)) return ConfigError("Simkl token missing");
// optional liveness probe:
//   var probe = await httpClient.GetAsync($"{settings.BaseUrl}/users/settings");
//   if (!probe.IsSuccessStatusCode) return ConfigError($"Simkl probe status {probe.StatusCode}");

Type guard

// Determine whether a Simkl response is safe to parse (status OK and not the html-block case).
bool IsParseableSimklResponse(ImportListResponse r)
    => r.HttpResponse.StatusCode == HttpStatusCode.OK
       && !(r.HttpResponse.Headers.ContentType?.Contains("text/json") == true
            && r.HttpRequest.Headers.Accept?.Contains("text/json") != true);

Try / catch

try { return _parser.ParseResponse(listResponse); }
catch (ImportListException ex) when (ex.Message.Contains("Simkl API call"))
{
    var code = listResponse.HttpResponse.StatusCode;
    if (code == HttpStatusCode.Unauthorized || code == HttpStatusCode.Forbidden)
        throw new ImportListException(listResponse, "Simkl auth failed ({0}). Re-link the account.", code);
    if ((int)code == 429 || (int)code >= 500)
        return Array.Empty<ImportListResponse>(); // transient: treat as empty this run
    throw;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Radarr/Radarr@ca451608dc (2026-08-13). Data as JSON: /api/errors/86f3f8ba2b37f9a4. Report an issue: GitHub.