Node.js 24.21.0 の MIMEType.parse で Content-Type を try/catch なしに扱う

外部から受け取った Content-Type をパースする処理は、地味に例外源になります。Node.js には標準で util.MIMEType がありますが、コンストラクタは不正な入力で TypeError を投げるため、素直に書くと必ず try / catch が付きます。

2026年9月8日にリリースされた Node.js 24.21.0 (LTS) で、これを投げない形に書き換えられる静的メソッドが入りました。リリースノートの Notable Changes に次の1行があります。

1
(SEMVER-MINOR) util: add non-throwing `MIMEType.parse` (James M Snell) #64965

何が変わるのか

公式 API ドキュメントによれば、MIMEType.parse(string) の戻り値は <MIMEType> | <null> で、不正な入力では例外ではなく null を返します。コンストラクタ側の挙動は従来どおりで、不正な入力には TypeError が投げられます。

つまり書き分けはこうなります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { MIMEType } from 'node:util';

// 従来:投げるので囲う必要がある
let mime;
try {
mime = new MIMEType(header);
} catch {
mime = null;
}

// 24.21.0 以降:戻り値で分岐できる
const mime2 = MIMEType.parse(header);
if (mime2 === null) {
// 不正な Content-Type
}

差は小さく見えますが、例外を制御フローに使わずに済むのが効きます。とくにリクエストごとに未検証のヘッダを見るサーバ側では、try ブロックが入れ子になるのを避けられます。

まず手元の版で挙動を確かめる

いきなり parse を書く前に、自分の Node にその静的メソッドがあるかを確認してください。筆者の環境は v24.11.1 で、次のようになりました。

1
2
3
4
const util = require('node:util');
console.log('node', process.version);
console.log('MIMEType?', typeof util.MIMEType);
console.log('MIMEType.parse?', util.MIMEType ? typeof util.MIMEType.parse : 'n/a');

実行結果です。

1
2
3
node v24.11.1
MIMEType? function
MIMEType.parse? undefined

クラス自体はあるのに、静的メソッド parseundefined です。24.21.0 で追加されたものなので、同じ 24 系でもそれ以前のパッチ版には入っていません。ここを確かめずに MIMEType.parse(...) と書くと TypeError: util.MIMEType.parse is not a function になり、「不正な MIME だったのか、メソッドが無かったのか」が区別しにくい形で落ちます

同じ v24.11.1 でコンストラクタ側を試すと、想定どおりの挙動でした。

1
2
3
4
5
6
const m = new util.MIMEType('text/html; charset=utf-8');
console.log(m.type, m.subtype, m.params.get('charset'), String(m));
// => text html utf-8 text/html;charset=utf-8

new util.MIMEType('not a mime');
// => TypeError: The MIME syntax for a type in "not a mime" is invalid at 3

注目したいのは正規化です。入力の text/html; charset=utf-8 は、文字列化すると text/html;charset=utf-8(セミコロン後の空白が落ちる) になります。ヘッダ値を比較したり、そのまま別のレスポンスへ転記したりするときに、元の文字列とは一致しないことを見込んでおいてください。

エラーメッセージが invalid at 3位置まで出すのも実測で分かった点です。ログに出す場合、この位置情報は入力のどこで壊れたかの手がかりになります。

版差を吸収して書く

24.21.0 未満も動かす必要があるなら、片方向のフォールバックで吸収できます。parse があればそれを、無ければコンストラクタを囲って null に寄せる形です。

1
2
3
4
5
6
7
8
9
const { MIMEType } = require('node:util');

const parseMime =
typeof MIMEType.parse === 'function'
? (s) => MIMEType.parse(s)
: (s) => { try { return new MIMEType(s); } catch { return null; } };

console.log(parseMime('text/html; charset=utf-8')?.subtype); // html
console.log(parseMime('not a mime')); // null

このコードは v24.11.1 で実行し、上記の出力になることを確認しています。呼び出し側は常に null 判定だけを書けばよくなるので、後日ランタイムを上げたときに呼び出し側を直す必要がありません。

使いどころと、使わないほうがよいところ

使いどころは、外から来る値を扱うときです。アップロードされたファイルの Content-Type、fetch のレスポンスヘッダ、設定ファイルに書かれた MIME 文字列など、壊れていることが想定内の入力に向きます。

逆に、自分が定数として持っている MIME 文字列にまで parse を使って null 分岐を書くのは過剰です。そこが null になるのはコードのバグであって、実行時に握りつぶすべき状況ではありません。コンストラクタが投げてくれたほうが、原因に早く辿り着けます。

もう1点。MIMEType は MIME の構文を検証するだけで、その MIME が実在するタイプかどうかは見ません。foo/bar は構文としては妥当なので、parsenull を返さずにオブジェクトを返します。実在タイプの検証が必要なら、それは別の層の仕事です。

まとめ

  • new MIMEType() は不正入力で TypeErrorMIMEType.parse()null
  • parseNode.js 24.21.0 (LTS, 2026-09-08) で追加された(PR #64965)。同じ 24 系でも v24.11.1 には無い
  • 導入前に typeof MIMEType.parse === 'function' で存在確認をする。無い版で呼ぶと、原因の分かりにくい TypeError になる
  • 文字列化ではセミコロン後の空白が落ちるため、元のヘッダ文字列との一致は前提にしない

まずやることは1つです。node -e "console.log(typeof require('node:util').MIMEType.parse)" を実行して、自分の環境が function を返すかを確かめてください。undefined なら、この記事のフォールバック版から入るのが安全です。


Node.js 24.21.0 の MIMEType.parse で Content-Type を try/catch なしに扱う
https://blog.hashito.biz/2026/09/22/nodejs-24-21-mimetype-parse-non-throwing/
著者
hashito
作成日
2026年9月22日
著作権

このバージョンはその範囲に入るのか

依存の話でいちばん間違えやすいのは ^1.2.3~1.2.3 がどこまでを許すかです(^0.2.3 のようにメジャーが 0 のときは規則が変わります)。範囲とバージョンを貼ると、下限・上限に展開したうえで一致・不一致とその理由を表示するsemver 範囲判定ツールを置いています。ブラウザの中だけで動き、入力はどこにも送信しません。