AI は仕様を知っているのに既定のエラーを書く。RFC 9457 を規程に書き下す 4 項目
はじめに
この稿が答えるのは、日本のチームが今週抱えそうな質問です。規程書に「エラーレスポンスの形式を統一する」と書いてあるのに、AI が作った API はエンドポイントごとに別の形のエラーを返している。では、その規程の項目をどう書けばよいのか。
2026 年 11 月 12 日に Zenn で公開された検証記事(tkydev 氏、以下「検証記事」)は、この問題の実験データを持っています。仕様を正しく説明できる同じ AI が、実際に作らせるとフレームワークの既定のエラーを書く。本篇は、規程を作る側が「統一する」を 4 項目に書き下す型を、その実験結果から整理します。
背景:知っていることと書くことの差
検証記事の出発点は、mattn 氏の X 上のポストです。ユーザーが見つからないときのエラーを 2 形で並べ、どちらを承認するかを問うものです。設計 A は 200 OK を返し、本文の success フラグで失敗を伝えます。設計 B は 404 Not Found を返し、本文にメッセージを入れます。ステータスコードの使い方としては B が正しい。mattn 氏の指摘は、B でも厳密には足りない、というものです。足りないのは標準のメディアタイプと本文の形です。
RFC 9457(HTTP API の問題詳細を定義する IETF 標準)は 2023 年 7 月に公開され、2016 年の RFC 7807 を置き換えました。エラー本文の 5 つのメンバーと、application/problem+json というメディアタイプを定義しています。5 つのメンバーは type や title や status や detail や instance です。type は問題の種類を識別し、detail はその発生に関する説明を運びます。この分離が重要です。クライアントは type で分岐し、人は detail を読みます。
検証記事は 2 つのモデルと 4 つのフレームワークで実験しています。Claude と ChatGPT、Express、FastAPI、Spring Boot、ASP.NET Core です。回数を重ねる前に毎回新しい会話にし、メモリやカスタム指示の影響を排しています。
実験:説明はできる、書くは別の話
「RFC 9457 を説明してください」への答えは、2 つのモデルとも正しかったです。5 つのメンバー、about:blank の意味、本文の status を実際のステータスと一致させる要件まで、内容は合っていました。
「設計 A と B のどちらを承認しますか」への答えは分かれました。Claude は 2 回とも B はそのままでは承認しないと答え、RFC 9457 形式への書き直しを示しました。ChatGPT は 2 回とも B を承認し、RFC 9457 には触れませんでした。
問題は作らせるときです。「{フレームワーク} でユーザーを ID で取得する REST API を実装してください」を、4 フレームワーク × 3 回 × 2 モデルで実行しました。生成コードは Docker で起動し、4 種のリクエストを送っています。4 種は、存在しない ID の取得、形式違いの ID、定義されていないパス、定義されていないメソッドです。
結果は 24 回中 5 回です。RFC 9457 の形式、つまり 404 と application/problem+json を返したのは 5 件のみでした。Claude の Spring Boot 3 回と ASP.NET Core 2 回です。ChatGPT は 12 回とも使っていません。レビューでは C を勧めた Claude が、Express では独自形式を 3 回とも書きました。
フレームワーク既定が勝つ理由
AI が書くのは仕様ではなく、そのフレームワークで書き慣れた形です。FastAPI の 6 回はすべて HTTPException を投げるだけで、それが {"detail": ...} の形になるため、6 回とも同じ形でした。
ASP.NET Core は分かれます。引数なしの NotFound() は ProblemDetails に変換されますが、メッセージを渡すと変換の対象外になり、渡したオブジェクトがそのまま本文になります。つまり「親切にメッセージを足す」ことが、標準の形から外す方向に働きます。Spring Boot は、RFC 9457 形式を有効にする spring.mvc.problemdetails.enabled が既定でオフです。
検証記事の中で最も注意を引いたのは、本文とヘッダが割れている 1 件です。ASP.NET Core の 1 回は、本文は RFC 9457 の形なのに Content-Type が application/json でした。コードを読む限りでは適合していると判定した形です。実際に起動して初めて分かりました。原因は、.NET 10 の TypedResults.NotFound() が値を受け取ると空のメンバーを埋め、メディアタイプを指定しない点です。JSON の既定である application/json になります。この挙動は 2024 年 10 月に Issue #58574 として報告されています。.NET 11(2026 年 11 月 10 日公開)では、PR #66461 で application/problem+json で書き出すように変わります。検証記事はソースコードの確認でそう述べています。ただし .NET 11 では起動していません。
ここから得る教訓は、AI が書いた API のレビューで「コードを読めばよい」が通用しない、ということです。実際にリクエストを送り、Content-Type まで確かめるのが確実な検証です。
規程への書き下し:4 項目
検証記事の追加実験では、プロンプトに「エラーレスポンスは RFC 9457 に従ってください」の 1 行を足した 6 回が、6 回とも標準の形になりました。404 だけでなく、フレームワークが自動で返すエラーも対象です。FastAPI の 422 検証エラーや未定義メソッドの 405 まで、指示した 6 回とも標準の形でした。足した 1 行には application/problem+json もメンバー名も書いていません。最も短い形で、AGENTS.md や CLAUDE.md にそのまま書ける指示です。
この結果から、規程の型として 4 項目が引けます。
- AI 向けの規則ファイルに指示を 1 行書く。「エラーレスポンスは RFC 9457 に従ってください」を最小とする。
- フレームワークの設定で済むものは設定にする。Spring Boot は
spring.mvc.problemdetails.enabled=trueを設定する。ASP.NET Core はAddProblemDetails()とUseStatusCodePages()を登録する。Express と FastAPI は組み込みの仕組みが無く、エラーハンドラーを自前で書く。 typeを管理する。typeがabout:blankのままだと、ステータスコード以上の情報を持たず、問題ごとに対応を分けられない。対応を分けたい種類には独自のtypeURI を定義し、文書化する。- 検証手順を「起動してリクエストを送る」にする。コードの読解は
Content-Typeの不一致を見逃す。確認するリクエストは、存在しない ID、形式違いの ID、未定義パス、未定義メソッドの 4 種を揃える。
項目 1 と 2 は方向が異なります。1 は AI に書いてもらうための指示、2 はフレームワーク自身が出力するための設定です。2 がある場合は AI が関与しない API にも効きます。項目 3 はクライアント側の契約、項目 4 は検証の関門です。
まとめ
この検証のギャップは、AI が仕様を知らないことではありません。2 つのモデルとも正しく説明し、Claude はレビューでその形式を勧めます。欠けているのは「既定で使う」という一文の置き場所です。書きの段階ではフレームワークの既定が勝ち、コードを読む限りは適合しているように見えます。
規程書の「エラーレスポンスの形式を統一する」は、上 4 項目を自チームのフレームワーク名で埋めた形に書き下してください。指示 1 行と検証手順が規則に入っていれば、「AI が別の形を使った」は信任の問題ではなく、レビューで指摘できる問題になります。
参考情報
- tkydev「AIは知っている仕様をなぜ使わない(RFC 9457でコード生成を検証した)」Zenn、2026 年 11 月 12 日公開。本稿の一次資料。24 回中 5 回、6 回とも一致、各フレームワークの挙動はすべてこの記事からの転記である。
- mattn 氏の X 上のポスト。検証記事のきっかけとなった 2 形比較のポスト。内容は検証記事経由の書き起こしで、ポスト本文の直接確認はしていない(二次引用)。
- RFC 9457
Problem Details for HTTP APIs(IETF 標準、HTTP API の問題詳細の定義)、2023 年 7 月。RFC 7807 を置き換える標準。正式名はコード表記で英文のまま。 - .NET の
Issue #58574とPR #66461。Content-Type挙動の報告と修正。検証記事経由の転記で、ソースの直接確認はしていない。
本稿は AI の支援を受けて執筆されました。事実の確認と内容の責任は筆者にあります。