{"record":{"id":"c834265ca6637831","repo":"Radarr/Radarr","slug":"invalid-extension-must-be-one-of-validextension","errorCode":null,"errorMessage":"Invalid extension, must be one of: {ValidExtensions.Join(\", \")}","messagePattern":"Invalid extension, must be one of: (.+?)","errorType":"http","errorClass":"UnsupportedMediaTypeException","httpStatus":415,"severity":"error","filePath":"src/Radarr.Api.V3/System/Backup/BackupController.cs","lineNumber":110,"sourceCode":"        }\n\n        [HttpPost(\"restore/upload\")]\n        [RequestFormLimits(MultipartBodyLengthLimit = 5000000000)]\n        public object UploadAndRestore()\n        {\n            var files = Request.Form.Files;\n\n            if (files.Empty())\n            {\n                throw new BadRequestException(\"file must be provided\");\n            }\n\n            var file = files[0];\n            var extension = Path.GetExtension(file.FileName);\n\n            if (!ValidExtensions.Contains(extension))\n            {\n                throw new UnsupportedMediaTypeException($\"Invalid extension, must be one of: {ValidExtensions.Join(\", \")}\");\n            }\n\n            var path = Path.Combine(_appFolderInfo.TempFolder, $\"radarr_backup_restore{extension}\");\n\n            _diskProvider.SaveStream(file.OpenReadStream(), path);\n            _backupService.Restore(path);\n\n            // Cleanup restored file\n            _diskProvider.DeleteFile(path);\n\n            return new\n            {\n                RestartRequired = true\n            };\n        }\n\n        private string GetBackupPath(NzbDrone.Core.Backup.Backup backup)\n        {","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/Radarr/Radarr/blob/ca451608dc60c6cec754aba8d96bfa30e9468ed5/src/Radarr.Api.V3/System/Backup/BackupController.cs#L92-L128","documentation":"Thrown as an UnsupportedMediaTypeException (HTTP 415) by the POST /api/v3/system/backup/restore/upload endpoint when the uploaded multipart file's extension is not in the hardcoded whitelist {\".zip\", \".db\", \".xml\"}. Radarr only restores archives in the three formats its own backup writer produces. The check at BackupController.cs:106-110 uses Path.GetExtension(file.FileName) (which includes the leading dot and returns \"\" for extensionless names) tested with List<string>.Contains, a case-sensitive comparison, so an uppercase extension like .ZIP or .Xml also fails.","triggerScenarios":"Uploading a backup file with no extension (Path.GetExtension returns \"\"), a wrong extension (.bak, .tar, .7z), or a compound extension where only the last segment is inspected (.tar.gz yields \".gz\"). Also any uppercase variant such as .ZIP, .DB, .XML because List<string>.Contains is ordinal/case-sensitive and the whitelist is all-lowercase.","commonSituations":"Restoring a backup downloaded from another Radarr instance that was renamed during transfer; restoring a manually zipped archive produced by a different tool; OS-level 'hide extensions' settings causing a user to append .zip to a file already named .zip (producing .zip.zip, which still passes); cross-platform restores where the file got a capitalized extension through a GUI rename.","solutions":["Rename the file so its extension is exactly one of .zip, .db, or .xml, all lowercase (e.g. mybackup.zip).","If the file is a .tar.gz or other archive, repackage it as a .zip before upload.","Confirm the extension is truly lowercase by checking with `ls` or `dir`; macOS/Windows 'hide extensions' can mask a .ZIP rename.","If you genuinely need a new backup format supported, add it to the ValidExtensions list in src/Radarr.Api.V3/System/Backup/BackupController.cs:23 and ensure _backupService.Restore can parse it."],"exampleFix":"// before: uploading radarr_backup_2024.BAK -> 415 Invalid extension\n// after: rename the file and re-upload\ncp radarr_backup_2024.BAK radarr_backup_2024.zip\ncurl -X POST http://localhost:7878/api/v3/system/backup/restore/upload \\\n  -H \"X-Api-Key: $API_KEY\" \\\n  -F \"file=@radarr_backup_2024.zip\"","handlingStrategy":"validation","validationCode":"// Client-side: check the file extension before posting\nvar allowed = new[] { \".zip\", \".db\", \".xml\" };\nvar ext = Path.GetExtension(filePath).ToLowerInvariant(); // mirror the server's lowercase whitelist\nif (!allowed.Contains(ext))\n{\n    throw new InvalidOperationException(\n        $\"File extension '{ext}' not supported. Allowed: {string.Join(\", \", allowed)}\");\n}\n// then upload\nusing var form = new MultipartFormDataContent();\nusing var fs = File.OpenRead(filePath);\nform.Add(new StreamContent(fs), \"file\", Path.GetFileName(filePath));\nvar resp = await client.PostAsync(\"api/v3/system/backup/restore/upload\", form);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always download backups directly from a Radarr GET /api/v3/system/backup response and restore them unchanged so the extension matches the whitelist.","Use Path.GetExtension (not a string EndsWith) when validating, to exactly match the server's logic.","Lowercase the extension client-side because the server's List<string>.Contains is case-sensitive.","Avoid renaming backups through GUIs that hide extensions; verify with ls/dir before upload."],"tags":["backup","restore","file-upload","validation","file-extension","unsupported-media-type"],"backgroundTag":null,"analyzedSha":"ca451608dc60c6cec754aba8d96bfa30e9468ed5","analyzedAt":"2026-08-13T17:21:54.115Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}