Node 24.21 MIMEType.parse: validate a Sume artifact content type

Node 24.21.0 adds a non-throwing MIMEType.parse. Use it, with a try/catch fallback for older Node, to check a Sume artifact's content_type before saving.

5 min readSume
All posts

To check the content type of a file a Sume job returned, parse the artifact's content_type with util.MIMEType and compare its type and essence. On Node 24.21.0, a MIMEType.parse() that does not throw is available for this (Node.js 24.21.0 release notes, read 2026-10-04). On earlier Node lines, new MIMEType(...) inside a try block does the same job, so the code below works on both.

The release is the September 8 LTS update. Among the items in its notes are OpenSSL 3.5.8, Undici 7.29.1 and the non-throwing parse. Only the last one matters here: it turns a malformed header from an exception into a value you can branch on.

Where the content type comes from

A completed Sume job lists artifacts in result.artifacts. Each has an id, a url on media.sume.com, a type such as image, and a content_type such as image/png (Jobs and results). You asked for an image, so the check is cheap insurance: before writing bytes to a path with an extension, confirm the declared type matches what you expect to store.

The image models accept an output_format of png, jpeg, webp or svg (Image models), so the extension is not fixed by your pipeline. Deriving it from the artifact's declared type, instead of from what you asked for, keeps a file's name honest. Some model responses name the field media_type rather than content_type, so read whichever your route returns.

A parse helper that works on Node 22 and 24

The helper below prefers the non-throwing static method when it exists and otherwise falls back to the constructor. It returns null for anything that is not a valid MIME type, and the caller maps the essence to a file extension.

import { MIMEType } from "node:util";

export function parseMime(value: unknown): MIMEType | null {
  if (typeof value !== "string") return null;
  const anyMime = MIMEType as unknown as { parse?: (v: string) => MIMEType | null };
  if (typeof anyMime.parse === "function") return anyMime.parse(value) ?? null;
  try {
    return new MIMEType(value);
  } catch {
    return null;
  }
}

const EXT: Record<string, string> = {
  "image/png": "png",
  "image/jpeg": "jpg",
  "image/webp": "webp",
  "video/mp4": "mp4",
  "audio/mpeg": "mp3",
};

export function extensionFor(artifact: { type?: string; content_type?: string }) {
  const mime = parseMime(artifact.content_type);
  if (!mime) return null;
  if (artifact.type && mime.type !== artifact.type) return null;
  return EXT[mime.essence] ?? null;
}

console.log(extensionFor({ type: "image", content_type: "image/png" }));
console.log(extensionFor({ type: "image", content_type: "not a mime" }));

What to do on a mismatch

  • Do not guess an extension. Store the file under the artifact id, log the job_id, and flag it for review.
  • Keep the original url from the job record rather than rebuilding it, since Sume media URLs from the result are the public output.
  • Pin Node in CI so the branch you test is the branch you ship. The code above runs on both paths, but only one is exercised per runtime.
  • Treat a missing content_type as unknown, not as a default image type.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume