{"record":{"id":"8f92cf2b4995a376","repo":"gotify/server","slug":"invalid-file-extension","errorCode":null,"errorMessage":"invalid file extension","messagePattern":"invalid file extension","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"api/application.go","lineNumber":433,"sourceCode":"\t\t\tfile, err := ctx.FormFile(\"file\")\n\t\t\tif err == http.ErrMissingFile {\n\t\t\t\tctx.AbortWithError(400, errors.New(\"file with key 'file' must be present\"))\n\t\t\t\treturn\n\t\t\t} else if err != nil {\n\t\t\t\tctx.AbortWithError(500, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\thead := make([]byte, 261)\n\t\t\topen, _ := file.Open()\n\t\t\topen.Read(head)\n\t\t\tif !filetype.IsImage(head) {\n\t\t\t\tctx.AbortWithError(400, errors.New(\"file must be an image\"))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\text := filepath.Ext(file.Filename)\n\t\t\tif !ValidApplicationImageExt(ext) {\n\t\t\t\tctx.AbortWithError(400, errors.New(\"invalid file extension\"))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tname := generateNonExistingImageName(a.ImageDir, func() string {\n\t\t\t\treturn generateImageName() + ext\n\t\t\t})\n\n\t\t\terr = ctx.SaveUploadedFile(file, a.ImageDir+name)\n\t\t\tif err != nil {\n\t\t\t\tctx.AbortWithError(500, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif app.Image != \"\" {\n\t\t\t\tos.Remove(a.ImageDir + app.Image)\n\t\t\t}\n\n\t\t\tapp.Image = name","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/api/application.go#L415-L451","documentation":"Even when the bytes pass the image sniff, the endpoint validates the filename extension against ValidApplicationImageExt. If filepath.Ext(file.Filename) is not in the allowlist (e.g. .svg, .bmp, .tiff, or no extension), the upload is rejected with this 400 error.","triggerScenarios":"Uploading a valid image whose filename extension is not whitelisted (e.g. .svg, .avif, .heic, or extensionless files); uploading with a filename that lost its extension during client processing.","commonSituations":"Browsers/Safari saving images as .jfif or .heic; files downloaded without extensions; animated SVG uploads; automation generating temp files without extensions.","solutions":["Convert/rename the file to an allowlisted extension (typically .png, .jpg, .jpeg, .gif) before uploading","Ensure the client preserves the original filename extension in the multipart part","If the format should be allowed, extend the ValidApplicationImageExt allowlist server-side (and consider serving implications)","Check the filename the client sends — some clients send 'blob' as the name"],"exampleFix":"// before\nfd.append('file', file, 'image');\n// after\nfd.append('file', file, 'image.png');","handlingStrategy":"validation","validationCode":"const allowed = ['.png', '.jpg', '.jpeg', '.gif'];\nconst ext = file.name.slice(file.name.lastIndexOf('.')).toLowerCase();\nif (!allowed.includes(ext)) throw new Error(`extension ${ext} not allowed`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep original filenames with extensions through the upload pipeline","Convert heic/jfif/avif images to png/jpg before uploading","Never send files named 'blob' or without extension from generic clients","Confirm the allowlist in ValidApplicationImageExt and match it in client-side checks"],"tags":["http-400","file-upload","validation","gin","go"],"backgroundTag":"file-extension-not-allowed","analyzedSha":"14bfc256276775c425f988d621dccfe705de18ac","analyzedAt":"2026-09-05T12:52:36.781Z","contentChangedAt":"2026-09-05T12:52:36.781Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}