Sonarr/Sonarr · warning · BadRequestException
seriesId or episodeIds must be provided
Error message
seriesId or episodeIds must be provided
What it means
Returned by GET /api/v3/episode when none of seriesId, episodeIds, or episodeFileId are supplied. The endpoint requires a scope to query episodes; with no scope it falls through every conditional branch and throws BadRequestException (HTTP 400). It prevents an expensive unbounded episode query.
Source
Thrown at src/Sonarr.Api.V3/Episodes/EpisodeController.cs:48
if (seriesId.HasValue)
{
if (seasonNumber.HasValue)
{
return MapToResource(_episodeService.GetEpisodesBySeason(seriesId.Value, seasonNumber.Value), includeSeries, includeEpisodeFile, includeImages);
}
return MapToResource(_episodeService.GetEpisodeBySeries(seriesId.Value), includeSeries, includeEpisodeFile, includeImages);
}
else if (episodeIds.Any())
{
return MapToResource(_episodeService.GetEpisodes(episodeIds), includeSeries, includeEpisodeFile, includeImages);
}
else if (episodeFileId.HasValue)
{
return MapToResource(_episodeService.GetEpisodesByFileId(episodeFileId.Value), includeSeries, includeEpisodeFile, includeImages);
}
throw new BadRequestException("seriesId or episodeIds must be provided");
}
[RestPutById]
[Consumes("application/json")]
public ActionResult<EpisodeResource> SetEpisodeMonitored([FromRoute] int id, [FromBody] EpisodeResource resource)
{
_episodeService.SetEpisodeMonitored(id, resource.Monitored);
resource = MapToResource(_episodeService.GetEpisode(id), false, false, false);
return Accepted(resource);
}
[HttpPut("monitor")]
[Consumes("application/json")]
public IActionResult SetEpisodesMonitored([FromBody] EpisodesMonitoredResource resource, [FromQuery] bool includeImages = false)
{
if (resource.EpisodeIds.Count == 1)View on GitHub (pinned to da2284d7ea)
Solutions
- Supply ?seriesId=<id> for all episodes of a series.
- Or ?episodeIds=1&episodeIds=2 for specific episodes.
- Or ?episodeFileId=<id> for episodes tied to a file.
- Validate that at least one filter is non-empty in the client before sending.
Example fix
// before
fetch('/api/v3/episode')
// after
fetch('/api/v3/episode?seriesId=' + seriesId) Defensive patterns
Strategy: validation
Validate before calling
const params = new URLSearchParams();
if (seriesId) params.set('seriesId', seriesId);
if (episodeIds?.length) episodeIds.forEach(id => params.append('episodeIds', id));
if (episodeFileId) params.set('episodeFileId', episodeFileId);
if (![...params.keys()].length) throw new Error('seriesId, episodeIds, or episodeFileId required');
fetch('/api/v3/episode?' + params); Type guard
function hasEpisodeScope(p){ return p.seriesId != null || (Array.isArray(p.episodeIds) && p.episodeIds.length>0) || p.episodeFileId != null; } Prevention
- Require exactly one of the three scopes client-side before request.
- Double-check query parameter names (episodeIds vs episodeId vs episodeFileId).
When it happens
Trigger: GET /api/v3/episode with an empty query string, or with parameters that don't match any of the three supported filter names. Sending episodeIds=[] alongside no seriesId also reaches this throw.
Common situations: A client defaulting all filters to null/empty on initial load; a typo in a query parameter name (e.g. episodeId instead of episodeIds); an integration test hitting the bare endpoint.
Related errors
- seriesId or episodeFileIds must be provided
- seriesIds must be provided
- seriesIds must be positive integers
- Unable to parse
- items must be provided
AI-assisted analysis of Sonarr/Sonarr@da2284d7ea (2026-08-13).
Data as JSON: /api/errors/9a648e85fb898947.
Report an issue: GitHub.