{"record":{"id":"04b7cc9b965ea15d","repo":"Sonarr/Sonarr","slug":"failed-to-restore-backup-response-statustext","errorCode":null,"errorMessage":"Failed to restore backup: ${response.statusText}","messagePattern":"Failed to restore backup: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/System/Backup/useBackups.ts","lineNumber":92,"sourceCode":"    Error,\n    FormData\n  >({\n    mutationFn: async (formData: FormData) => {\n      const response = await fetch(\n        `${window.Sonarr.urlBase}/api/v5/system/backup/restore/upload`,\n        {\n          method: 'POST',\n          headers: {\n            'X-Api-Key': window.Sonarr.apiKey,\n            'X-Sonarr-Client': 'Sonarr',\n            // Don't set Content-Type, let browser set it with boundary for multipart/form-data\n          },\n          body: formData,\n        }\n      );\n\n      if (!response.ok) {\n        throw new Error(`Failed to restore backup: ${response.statusText}`);\n      }\n\n      return response.json();\n    },\n    onSuccess: () => {\n      queryClient.invalidateQueries({ queryKey: ['/system/backup'] });\n    },\n  });\n\n  return {\n    uploadBackup: mutate,\n    isUploadingBackup: isPending,\n    uploadBackupError: error,\n  };\n};\n","sourceCodeStart":74,"sourceCodeEnd":108,"githubUrl":"https://github.com/Sonarr/Sonarr/blob/da2284d7eaab22ed9b2ca698af690148d691e967/frontend/src/System/Backup/useBackups.ts#L74-L108","documentation":"Thrown by the base REST controller's ValidateId guard whenever a resource ID is <= 0. Sonarr identifies every resource with a positive integer; zero and negative values are not real records and signal a malformed or uninitialized request. ValidateId is inherited by all REST resource controllers, so this guard protects every /api/{resource}/{id} route that calls it.","triggerScenarios":"Any GET/PUT/DELETE (or resource read/modify) against a REST resource endpoint where the {id} resolves to 0 or a negative number; a route where the id segment fails to bind and defaults to 0; a client building the URL from an unset variable; an action that chains ValidateId on an id sourced from an empty collection or failed lookup.","commonSituations":"Client code that uses an uninitialized/default int id (0) before it has been populated from a create/list call; deleting or updating a resource whose id was never fetched; URL templating that leaves a placeholder unsubstituted (e.g. /series/0); off-by-one after treating a list index as the resource id; chained calls where a previous step returned no item but its (default) id was passed forward.","solutions":["Make sure the id originates from a successful prior create or list response and is a positive integer before issuing the request.","Validate id > 0 client-side and short-circuit (or surface a clear error) before constructing the URL.","Inspect URL construction for placeholder/template bugs that can produce /resource/0 or /resource/-1.","If the id came from a lookup that returned nothing, fix the upstream lookup rather than forwarding its default value.","Log the raw id value at the call site to confirm whether the problem is the source of the id or URL parsing."],"exampleFix":"// before\n//   await client.DeleteSeriesAsync(seriesId); // seriesId == 0 (uninitialized)\n//\n// after\n//   if (seriesId <= 0) throw new ArgumentException(\"seriesId must be set\", nameof(seriesId));\n//   await client.DeleteSeriesAsync(seriesId);","handlingStrategy":"validation","validationCode":"// Validate before any REST call that takes a resource id.\nstatic void EnsureValidId(int id, [CallerArgumentExpression(nameof(id))] string? name = null)\n{\n    if (id <= 0)\n        throw new ArgumentOutOfRangeException(name, id, \"Resource id must be a positive integer.\");\n}\n\n// usage\nEnsureValidId(seriesId);\nawait client.GetSeriesAsync(seriesId, ct);","typeGuard":"// Narrow/validate an id value sourced from untyped data (JSON, query) before use.\nstatic bool IsValidResourceId(object? value, out int id) =>\n    value is int i && i > 0 && (id = i) == i;\n\n// usage: if (!IsValidResourceId(payload[\"id\"], out var id)) return;","tryCatchPattern":"try\n{\n    await client.UpdateSeriesAsync(id, resource, ct);\n}\ncatch (ApiException ex) when (ex.StatusCode == HttpStatusCode.BadRequest &&\n                              ex.Message.Contains(\"is not a valid ID\"))\n{\n    // id was <= 0; treat as a programming error, not a transient failure.\n    throw new InvalidOperationException($\"Refusing to retry: id={id} is non-positive. Fix the id source.\", ex);\n}","preventionTips":["Treat a non-positive id as a programmer error: fail loud at the call boundary rather than letting it reach the server.","Always source ids from successful create/list responses; never reuse an id after the lookup that produced it failed.","Unit-test URL/template builders with an unset-id case to catch /resource/0 before runtime.","Centralize REST id handling in one client method that applies the > 0 guard so it cannot be skipped."],"tags":["rest-api","validation","route-params","id-handling"],"backgroundTag":null,"analyzedSha":"da2284d7eaab22ed9b2ca698af690148d691e967","analyzedAt":"2026-08-13T15:53:02.562Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}