From 40101c13fdd125d1bc2d5f54717d49ce24774e8e Mon Sep 17 00:00:00 2001 From: Vuk Marjanovic Date: Wed, 25 Feb 2026 00:20:58 +0100 Subject: [PATCH 1/2] feat: add error handling for FetchError --- server/utils/error-handler.ts | 8 +++++ test/unit/server/utils/error-handler.spec.ts | 31 ++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/server/utils/error-handler.ts b/server/utils/error-handler.ts index 2afeda853..9b020f608 100644 --- a/server/utils/error-handler.ts +++ b/server/utils/error-handler.ts @@ -1,4 +1,5 @@ import { isError, createError } from 'h3' +import { FetchError } from 'ofetch' import * as v from 'valibot' import type { ErrorOptions } from '#shared/types/error' @@ -18,6 +19,13 @@ export function handleApiError(error: unknown, fallback: ErrorOptions): never { throw error } + if (error instanceof FetchError && error.statusCode) { + throw createError({ + statusCode: error.statusCode, + message: error.statusMessage, + }) + } + // Handle Valibot validation errors if (v.isValiError(error)) { throw createError({ diff --git a/test/unit/server/utils/error-handler.spec.ts b/test/unit/server/utils/error-handler.spec.ts index 79024132f..262063628 100644 --- a/test/unit/server/utils/error-handler.spec.ts +++ b/test/unit/server/utils/error-handler.spec.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest' import { createError } from 'h3' +import { FetchError } from 'ofetch' import * as v from 'valibot' import { handleApiError } from '../../../../server/utils/error-handler' @@ -44,4 +45,34 @@ describe('handleApiError', () => { expect.objectContaining({ statusCode: 503, message: 'Service unavailable' }), ) }) + + describe('FetchError handling', () => { + it('propagates the upstream statusCode from a FetchError', () => { + const fetchErr = new FetchError('Not Found') + fetchErr.statusCode = 404 + fetchErr.statusMessage = 'Not Found' + + expect(() => handleApiError(fetchErr, fallback)).toThrow( + expect.objectContaining({ statusCode: 404, message: 'Not Found' }), + ) + }) + + it('propagates a 503 statusCode from a FetchError', () => { + const fetchErr = new FetchError('Service Unavailable') + fetchErr.statusCode = 503 + fetchErr.statusMessage = 'Service Unavailable' + + expect(() => handleApiError(fetchErr, fallback)).toThrow( + expect.objectContaining({ statusCode: 503 }), + ) + }) + + it('falls through to the generic fallback when FetchError has no statusCode', () => { + const fetchErr = new FetchError('Network error') + + expect(() => handleApiError(fetchErr, { message: 'Bad gateway', statusCode: 502 })).toThrow( + expect.objectContaining({ statusCode: 502, message: 'Bad gateway' }), + ) + }) + }) }) From c667bbdc24976886f8f48d6ac1193af4e2dfd9a0 Mon Sep 17 00:00:00 2001 From: Vuk Marjanovic Date: Wed, 25 Feb 2026 12:58:09 +0100 Subject: [PATCH 2/2] fix: pass statusMessage while throwing error --- server/utils/error-handler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/utils/error-handler.ts b/server/utils/error-handler.ts index 9b020f608..8ba47ea31 100644 --- a/server/utils/error-handler.ts +++ b/server/utils/error-handler.ts @@ -22,7 +22,8 @@ export function handleApiError(error: unknown, fallback: ErrorOptions): never { if (error instanceof FetchError && error.statusCode) { throw createError({ statusCode: error.statusCode, - message: error.statusMessage, + statusMessage: error.statusMessage, + message: error.message, }) }