ASP.NET Core の UseExceptionHandler が 413 を 500 に変えなくなります
2026-08-19 に dotnet/aspnetcore の main へ取り込まれた PR により、ExceptionHandlerMiddleware は BadHttpRequestException.StatusCode を 500 で上書きせず尊重するようになります。
本番環境で app.UseExceptionHandler() を使っている場合、Kestrel がサイズ超過で拒否したリクエストは、これまでテレメトリ上でサーバー障害として記録されてきました。PR #68632 が 2026-08-19 に dotnet/aspnetcore の main へ入り、これを修正します。2022 年 9 月に登録された issue #43831 をクローズするものです。
実体は 413 だった 500
ExceptionHandlerMiddleware は、あなたのハンドラーを呼び出す前にレスポンスのステータスコードを設定します。この PR までは、ExceptionHandlerOptions.StatusCodeSelector が null のとき 500 が固定で入っていました。BadHttpRequestException は独自の StatusCode を持っていますが、その値は捨てられていたのです。
ASP.NET Core 10.0.0、SDK 10.0.201 で確認した形は次のとおりです。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
builder.WebHost.ConfigureKestrel(k => k.Limits.MaxRequestBodySize = 100);
var app = builder.Build();
app.UseExceptionHandler();
app.MapPost("/upload", async (HttpContext ctx) =>
{
using var ms = new MemoryStream();
await ctx.Request.Body.CopyToAsync(ms); // throws when the body exceeds 100 bytes
return Results.Ok(ms.Length);
});
app.Run();
/upload に 500 バイトを POST してみてください。ミドルウェアに到達する例外は StatusCode = 413 を持つ BadHttpRequestException で、メッセージは “Request body too large. The max request body size is 100 bytes.” です。しかし実際に返ってくるレスポンスはこれです。
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json
{"type":"https://tools.ietf.org/html/rfc9110#section-15.6.1",
"title":"An error occurred while processing your request.","status":500,...}
クライアントは「あなたがサーバーを壊した」と伝えられます。5xx のダッシュボードも同じ見解です。ファイルアップロード時の 413 Request Entity Too Large と同種の混乱ですが、こちらは正しいステータスがそもそもネットワークまで届きません。
何が変わったのか
ミドルウェアは 500 にフォールバックする前に、例外に対してパターンマッチングを行うようになりました。
context.Response.StatusCode = _options.StatusCodeSelector?.Invoke(edi.SourceException)
?? (edi.SourceException switch
{
BadHttpRequestException badHttpRequestException => badHttpRequestException.StatusCode,
_ => DefaultStatusCode,
});
押さえておきたい点が 3 つあります。StatusCodeSelector を設定していればこれまでどおり優先されるので、既存の上書きは挙動が変わりません。独自の ExceptionHandler デリゲートや IExceptionHandler サービスは、その後でコードを変更できます。そして BadHttpRequestException が運ぶ 404 は、設定ミスのハンドラーではなく意図的なものとして扱われるようになり、生き残るために AllowStatusCode404Response = true を必要としなくなりました。
対象範囲は意図的に狭く、再マッピングされるのは BadHttpRequestException だけです。text/plain のボディで Request.ReadFormAsync() を呼ぶと InvalidOperationException (“Incorrect Content-Type”) が発生しますが、これは変更の前後どちらでも 500 のままです。minimal API のモデルバインドも影響を受けません。不正な JSON ボディは、例外が外へ抜ける前に request delegate によって素の 400 に変換されるからです。
執筆時点で、このコミットは main にのみ存在します。release/11.0-rc1 ブランチには入っていないため、RC1 ではなくそれ以降の .NET 11 ビルドで届くと考えてください。今日 .NET 8 から 11 を使っているなら、回避策は従来どおり、例外を自分で取り出す StatusCodeSelector です。
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.