Skip to content

body-limit: allow injecting the over-limit error (createError) in limitRequestBody / limitBodyStream #282

Description

@pi0x

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions