Follow-up to #281 (thanks for the fast fix — limitRequestBody preserving ServerRequest is exactly what we needed).
Problem
limitRequestBody / limitBodyStream always throw srvx's own ERR_BODY_TOO_LARGE (createBodyTooLargeError). A downstream framework with its own error type (h3's HTTPError) then has to re-recognize that foreign shape at every place a body is consumed:
- each body reader's catch (
readBody/formData, validated json(), JSON-RPC) must special-case ERR_BODY_TOO_LARGE so it isn't masked as a 400 parse error, and
- the central error→response mapper must special-case it so a legit
413 isn't logged as an unhandled 500.
That's a code === "ERR_BODY_TOO_LARGE" check sprinkled across ~4 files (incl. the hot error path), purely to translate the error identity. See h3js/h3#1500.
Proposal
Let the caller inject the error thrown on overflow, defaulting to today's behavior:
export function limitRequestBody<T extends Request>(
request: T,
maxRequestBodySize: number,
createError?: (maxRequestBodySize: number) => unknown, // default: createBodyTooLargeError
): T;
export function limitBodyStream(
stream: ReadableStream<Uint8Array>,
maxRequestBodySize: number,
createError?: (maxRequestBodySize: number) => unknown,
): ReadableStream<Uint8Array>;
createError replaces the two internal createBodyTooLargeError(maxRequestBodySize) call-sites (the Content-Length fast-path and the limitBodyStream overflow). limitRequestBody forwards it to limitBodyStream and to the clone() branch so clones stay limited with the same error. Signature stays symmetric with createBodyTooLargeError(max); fully backward-compatible (omit → unchanged).
Then h3 passes its own HTTPError and drops all the translation code — no isBodyLimitError helper, no changes to the core error path, no bundle-size bump, and a raw req.text() overflow throws the framework's native error directly:
req = limitRequestBody(req, limit, () =>
new HTTPError({ status: 413, statusText: "Request Entity Too Large",
message: `Request body size exceeds the limit of ${limit} bytes` }));
Generally useful beyond h3 — any consumer can map overflow to its own error/response type without string-matching a code. Happy to send the PR if you're open to the shape.
Follow-up to #281 (thanks for the fast fix —
limitRequestBodypreservingServerRequestis exactly what we needed).Problem
limitRequestBody/limitBodyStreamalways throw srvx's ownERR_BODY_TOO_LARGE(createBodyTooLargeError). A downstream framework with its own error type (h3'sHTTPError) then has to re-recognize that foreign shape at every place a body is consumed:readBody/formData, validatedjson(), JSON-RPC) must special-caseERR_BODY_TOO_LARGEso it isn't masked as a400parse error, and413isn't logged as an unhandled500.That's a
code === "ERR_BODY_TOO_LARGE"check sprinkled across ~4 files (incl. the hot error path), purely to translate the error identity. See h3js/h3#1500.Proposal
Let the caller inject the error thrown on overflow, defaulting to today's behavior:
createErrorreplaces the two internalcreateBodyTooLargeError(maxRequestBodySize)call-sites (theContent-Lengthfast-path and thelimitBodyStreamoverflow).limitRequestBodyforwards it tolimitBodyStreamand to theclone()branch so clones stay limited with the same error. Signature stays symmetric withcreateBodyTooLargeError(max); fully backward-compatible (omit → unchanged).Then h3 passes its own
HTTPErrorand drops all the translation code — noisBodyLimitErrorhelper, no changes to the core error path, no bundle-size bump, and a rawreq.text()overflow throws the framework's native error directly:Generally useful beyond h3 — any consumer can map overflow to its own error/response type without string-matching a code. Happy to send the PR if you're open to the shape.