{"record":{"id":"90b8de8db2c26ead","repo":"gitroomhq/postiz-app","slug":"unsupported-file-type-90b8de","errorCode":null,"errorMessage":"Unsupported file type.","messagePattern":"Unsupported file type\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"libraries/nestjs-libraries/src/upload/r2.uploader.ts","lineNumber":89,"sourceCode":"      return completeMultipartUpload(req, res);\n    case 'list-parts':\n      return listParts(req, res);\n    case 'abort-multipart-upload':\n      return abortMultipartUpload(req, res);\n    case 'sign-part':\n      return signPart(req, res);\n  }\n  return res.status(404).end();\n}\n\nexport async function simpleUpload(\n  data: Buffer,\n  originalFilename: string,\n  _contentType: string\n) {\n  const detected = await fileTypeFromBuffer(data);\n  if (!detected || !Object.values(ALLOWED_EXT_TO_MIME).includes(detected.mime)) {\n    throw new Error('Unsupported file type.');\n  }\n  const fileExtension = `.${detected.ext}`;\n  const safeContentType = detected.mime;\n  const randomFilename = generateRandomString() + fileExtension;\n\n  const params = {\n    Bucket: CLOUDFLARE_BUCKETNAME,\n    Key: randomFilename,\n    Body: data,\n    ContentType: safeContentType,\n  };\n\n  const command = new PutObjectCommand({ ...params });\n  await R2.send(command);\n\n  return CLOUDFLARE_BUCKET_URL + '/' + randomFilename;\n}\n","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/gitroomhq/postiz-app/blob/0f1647f7491a217d43eb5ae7a480484bdf0aff3e/libraries/nestjs-libraries/src/upload/r2.uploader.ts#L71-L107","documentation":"simpleUpload in r2.uploader.ts sniffs the buffer and requires the detected MIME to be one of the values in ALLOWED_EXT_TO_MIME before uploading to R2. It's the same content-based allow-list pattern used across the upload stack, keyed off an extension-to-MIME map.","triggerScenarios":"Passing a buffer whose sniffed MIME isn't in ALLOWED_EXT_TO_MIME (svg, text, pdf, heic, unknown signature), an empty buffer, or a mismatch where the filename extension suggests one type but bytes say another.","commonSituations":"New upload call sites (downloads from provider APIs, generated files) forgetting the type map; ffmpeg/ffmpeg-less pipelines emitting containers not in the map; allow-list drift between this map and the ones in cloudflare.storage.ts / custom.upload.validation.ts.","solutions":["Sniff the buffer yourself (fileTypeFromBuffer) and log detected.mime to see what's rejected","Add the MIME value to ALLOWED_EXT_TO_MIME if it should be accepted","Fix the upstream producer that's emitting unexpected bytes (e.g. an API returning an error page instead of media)","Keep ALLOWED_EXT_TO_MIME in sync with ALLOWED_MIME_TYPES used elsewhere"],"exampleFix":"// before\nawait simpleUpload(buffer, 'clip.mov', 'video/quicktime'); // mov not in map -> throws\n\n// after\nconst ALLOWED_EXT_TO_MIME = { ..., mov: 'video/quicktime', ... };\nawait simpleUpload(buffer, 'clip.mov', 'video/quicktime');","handlingStrategy":"validation","validationCode":"import { fileTypeFromBuffer } from 'file-type';\nconst t = await fileTypeFromBuffer(data);\nif (!t || !Object.values(ALLOWED_EXT_TO_MIME).includes(t.mime)) {\n  throw new Error(`simpleUpload rejects ${t?.mime ?? 'unknown'}`);\n}","typeGuard":"const isSimpleUploadable = async (b: Buffer) => { const t = await fileTypeFromBuffer(b); return !!t && Object.values(ALLOWED_EXT_TO_MIME).includes(t.mime); };","tryCatchPattern":"try { await simpleUpload(buf, name, ct); } catch (e) { if ((e as Error).message === 'Unsupported file type.') logRejectedBuffer(buf); else throw e; }","preventionTips":["Sniff before uploading in new call sites","Keep ALLOWED_EXT_TO_MIME synced with other allow-lists","Log detected MIME on rejection"],"tags":["upload","r2","file-type","allow-list"],"backgroundTag":"unsupported-file-type","analyzedSha":"0f1647f7491a217d43eb5ae7a480484bdf0aff3e","analyzedAt":"2026-08-27T12:09:55.020Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}