{"record":{"id":"415fc2b80c77406d","repo":"n8n-io/n8n","slug":"content-not-viewable","errorCode":null,"errorMessage":"Content not viewable","messagePattern":"Content not viewable","errorType":"exception","errorClass":"BadRequestError","httpStatus":400,"severity":"warning","filePath":"packages/cli/src/controllers/binary-data.controller.ts","lineNumber":89,"sourceCode":"\t\t}\n\t}\n\n\tprivate async setContentHeaders(\n\t\tbinaryDataId: string,\n\t\taction: 'view' | 'download',\n\t\tres: Response,\n\t\tfileName?: string,\n\t\tmimeType?: string,\n\t) {\n\t\ttry {\n\t\t\tconst metadata = await this.binaryDataService.getMetadata(binaryDataId);\n\t\t\tfileName = metadata.fileName ?? fileName;\n\t\t\tmimeType = metadata.mimeType ?? mimeType;\n\t\t\tres.setHeader('Content-Length', metadata.fileSize);\n\t\t} catch {}\n\n\t\tif (action === 'view' && (!mimeType || !ViewableMimeTypes.includes(mimeType.toLowerCase()))) {\n\t\t\tthrow new BadRequestError('Content not viewable');\n\t\t}\n\n\t\tif (mimeType) {\n\t\t\tres.setHeader('Content-Type', mimeType);\n\t\t}\n\n\t\tres.setHeader('Content-Security-Policy', getHtmlSandboxCSP());\n\n\t\tif (action === 'download') {\n\t\t\tif (fileName) {\n\t\t\t\tconst encodedFilename = encodeURIComponent(fileName);\n\t\t\t\tres.setHeader('Content-Disposition', `attachment; filename=\"${encodedFilename}\"`);\n\t\t\t} else {\n\t\t\t\tres.setHeader('Content-Disposition', 'attachment');\n\t\t\t}\n\t\t}\n\t}\n}","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/controllers/binary-data.controller.ts#L71-L107","documentation":"BadRequestError (HTTP 400) 'Content not viewable' thrown at binary-data.controller.ts:89 when `action === 'view'` and the resolved MIME type is missing or not in `ViewableMimeTypes`. Viewable set: application/json; audio/{mpeg,ogg,wav}; image/{bmp,gif,jpeg,jpg,png,tiff,webp}; text/{css,csv,markdown,plain}; video/{mp4,ogg,webm}. HTML, SVG, and PDF are intentionally excluded (XSS / code-exec risk).","triggerScenarios":"Calling `GET /binary-data?id=...&action=view` on a binary whose MIME is `text/html`, `image/svg+xml`, `application/pdf`, an unknown type, or where no MIME is resolvable from metadata or query.","commonSituations":"Trying to inline-preview HTML/SVG/PDF outputs; attachments with missing/incorrect `mimeType`; frontend defaulting to `view` for unsafe types.","solutions":["Use `action=download` instead of `view` for non-viewable MIME types.","If the type should be viewable, set the correct `mimeType` query param or fix the binary metadata at write time.","Never request `view` for HTML/SVG/PDF — the allowlist will refuse by design."],"exampleFix":"// before\nGET /binary-data?id=filesystem:report.html&action=view\n// 400 Content not viewable\n\n// after\nGET /binary-data?id=filesystem:report.html&action=download","handlingStrategy":"validation","validationCode":"const VIEWABLE = ['application/json','audio/mpeg','audio/ogg','audio/wav','image/bmp','image/gif','image/jpeg','image/jpg','image/png','image/tiff','image/webp','text/css','text/csv','text/markdown','text/plain','video/mp4','video/ogg','video/webm'];\nfunction canView(mimeType?: string) {\n  return !!mimeType && VIEWABLE.includes(mimeType.toLowerCase());\n}\n// action = canView(mt) ? 'view' : 'download'","typeGuard":"const VIEWABLE = ['application/json','audio/mpeg','audio/ogg','audio/wav','image/bmp','image/gif','image/jpeg','image/jpg','image/png','image/tiff','image/webp','text/css','text/csv','text/markdown','text/plain','video/mp4','video/ogg','video/webm'] as const;\nconst isViewable = (m?: string): m is string => !!m && (VIEWABLE as readonly string[]).includes(m.toLowerCase());","tryCatchPattern":"try { await get(req); } catch (e) { if (e instanceof BadRequestError && /not viewable/.test(e.message)) {/* fall back to download */} }","preventionTips":["Default to `action=download` for unknown or unsafe MIME types.","Never request `view` for HTML/SVG/PDF — the allowlist refuses them by design.","Record mimeType on the binary at write time so the viewer can decide."],"tags":["binary-data","rest","bad-request","mime-type","security"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}