You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
EmDash's media upload route rejects engineering file formats (STL, 3MF, OBJ, GLTF/GLB, STEP, etc.) even when a file field declares them in allowedMimeTypes. Browsers don't have registered MIME types for most of these — file dialog uploads of an .stl send Content-Type: application/octet-stream, so the per-field allowlist match in src/api/media.ts:119 (matchesMimeAllowlist(file.type, allowlist)) always fails against an allowedMimeTypes: ["model/stl"] config.
This came up while wiring an STL/3MF viewer plugin into a site. The current workaround is to add "application/octet-stream" to allowedMimeTypes, which is broader than intended (any binary now uploads).
Upload an .stl from a file dialog in Firefox/Chrome/Safari → 400 INVALID_TYPE. Browser sent application/octet-stream; allowlist doesn't include it; reject.
Suggested fix (two-part)
1. Extend EXTENSION_TO_MIME in src/media/mime.ts with common engineering / 3D formats, so schema authors can use the existing ".stl" shorthand and it actually expands:
These all have IANA-registered MIME types; the registry just doesn't reflect them yet. The model/* prefix would also let users say "model/" to widen the field for any 3D format.
2. Sniff extension as a fallback when browser sends application/octet-stream. In src/api/media.ts's POST handler, before the allowlist check:
constreportedMime=normalizeMime(file.type);constsniffedMime=reportedMime==="application/octet-stream"
? expandExtensionShorthand(path.extname(file.name).toLowerCase())
: null;consteffectiveMime=sniffedMime??reportedMime;if(!matchesMimeAllowlist(effectiveMime,allowlist)){returnapiError("INVALID_TYPE","File type not allowed",400);}
This is a small behavior change but matches user intent: when the schema author says "allow .stl uploads" and the file is in fact an .stl, accept it even though the browser couldn't identify the MIME. Filename is still client-supplied; the stored mimeType in the DB should also use the sniffed value so downstream consumers see consistent types.
Out of scope
Whether to widen GLOBAL_UPLOAD_ALLOWLIST is a separate question and probably stays restrictive.
The plugin-side rendering for these formats is the plugin's problem, not core's.
I have a viewer plugin shipping model/stl + model/3mf blocks (https://github.com/ebootheee/emdash-plugin-stl-viewer) and hit this trying to wire its upload path. Happy to put a PR together for both parts if there's appetite for the approach.
This discussion was converted from issue #1072 on May 17, 2026 06:52.
Heading
Bold
Italic
Quote
Code
Link
Numbered list
Unordered list
Task list
Attach files
Mention
Reference
Menu
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Problem
EmDash's media upload route rejects engineering file formats (STL, 3MF, OBJ, GLTF/GLB, STEP, etc.) even when a
filefield declares them inallowedMimeTypes. Browsers don't have registered MIME types for most of these — file dialog uploads of an.stlsendContent-Type: application/octet-stream, so the per-field allowlist match insrc/api/media.ts:119(matchesMimeAllowlist(file.type, allowlist)) always fails against anallowedMimeTypes: ["model/stl"]config.This came up while wiring an STL/3MF viewer plugin into a site. The current workaround is to add
"application/octet-stream"toallowedMimeTypes, which is broader than intended (any binary now uploads).Repro
Upload an
.stlfrom a file dialog in Firefox/Chrome/Safari → 400INVALID_TYPE. Browser sentapplication/octet-stream; allowlist doesn't include it; reject.Suggested fix (two-part)
1. Extend
EXTENSION_TO_MIMEinsrc/media/mime.tswith common engineering / 3D formats, so schema authors can use the existing".stl"shorthand and it actually expands:These all have IANA-registered MIME types; the registry just doesn't reflect them yet. The
model/*prefix would also let users say"model/"to widen the field for any 3D format.2. Sniff extension as a fallback when browser sends
application/octet-stream. Insrc/api/media.ts's POST handler, before the allowlist check:This is a small behavior change but matches user intent: when the schema author says "allow
.stluploads" and the file is in fact an.stl, accept it even though the browser couldn't identify the MIME. Filename is still client-supplied; the storedmimeTypein the DB should also use the sniffed value so downstream consumers see consistent types.Out of scope
GLOBAL_UPLOAD_ALLOWLISTis a separate question and probably stays restrictive.I have a viewer plugin shipping
model/stl+model/3mfblocks (https://github.com/ebootheee/emdash-plugin-stl-viewer) and hit this trying to wire its upload path. Happy to put a PR together for both parts if there's appetite for the approach.Beta Was this translation helpful? Give feedback.
All reactions