From 215d62b5e61f89b6f4d893aa400763bd1a12600f Mon Sep 17 00:00:00 2001 From: RaphaelFakhri <153192858+RaphaelFakhri@users.noreply.github.com> Date: Tue, 29 Sep 2026 17:41:41 +0300 Subject: [PATCH] fix: respond with 500 when a handler throws a non-Error value --- packages/micro/src/lib/index.ts | 13 ++++++++++--- test/suite/index.ts | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/packages/micro/src/lib/index.ts b/packages/micro/src/lib/index.ts index 8977b35..b70ad21 100644 --- a/packages/micro/src/lib/index.ts +++ b/packages/micro/src/lib/index.ts @@ -1,5 +1,6 @@ // Native import { Stream, Readable } from 'stream'; +import { inspect } from 'util'; // Packages import contentType from 'content-type'; import getRawBody from 'raw-body'; @@ -148,9 +149,15 @@ export const run = ( } }) .catch((err: unknown) => { - if (isError(err)) { - sendError(req, res, err); - } + // A handler can throw or reject with any value. Always answer with an + // error response, otherwise the request never completes. + sendError( + req, + res, + isError(err) + ? err + : new Error(`Handler threw a non-Error value: ${inspect(err)}`), + ); }); // Maps requests to buffered raw bodies so that diff --git a/test/suite/index.ts b/test/suite/index.ts index 563f77c..053db68 100644 --- a/test/suite/index.ts +++ b/test/suite/index.ts @@ -696,3 +696,26 @@ void test('Content-Type header for JSON is set', async (t) => { t.equal(res.headers.get('content-type'), 'application/json; charset=utf-8'); shutdown(); }); + +void test('throw (500) with a non-Error value', async (t) => { + const fn: RequestHandler = () => { + throw 'not an error object'; + }; + + const [url, shutdown] = await startServer(fn); + + const { status } = await fetch(url, { timeout: 2000 }); + t.same(status, 500); + shutdown(); +}); + +void test('rejected promise with a non-Error value sends 500', async (t) => { + // eslint-disable-next-line prefer-promise-reject-errors -- testing a non-Error rejection + const fn: RequestHandler = () => Promise.reject({ reason: 'plain object' }); + + const [url, shutdown] = await startServer(fn); + + const { status } = await fetch(url, { timeout: 2000 }); + t.same(status, 500); + shutdown(); +});