(
res: ServerResponse,
status: HttpErrorStatus,
type: ProblemType,
title: string,
options: ErrorResponseOptions = {},
)
| 57 | >; |
| 58 | |
| 59 | export function errorResponse( |
| 60 | res: ServerResponse, |
| 61 | status: HttpErrorStatus, |
| 62 | type: ProblemType, |
| 63 | title: string, |
| 64 | options: ErrorResponseOptions = {}, |
| 65 | ): void { |
| 66 | const instance = options.instance ?? `urn:uuid:${randomUUID()}`; |
| 67 | |
| 68 | if (res.headersSent || res.writableEnded || res.destroyed) { |
| 69 | log().error( |
| 70 | { |
| 71 | event: 'api.error.double-write', |
| 72 | instance, |
| 73 | type, |
| 74 | status, |
| 75 | handler: options.handler, |
| 76 | }, |
| 77 | 'errorResponse called after headers already sent — suppressed', |
| 78 | ); |
| 79 | apiErrorCounter().add(1, { |
| 80 | type: 'urn:ok:error:internal-server-error', |
| 81 | ...(options.handler ? { handler: options.handler } : {}), |
| 82 | }); |
| 83 | return; |
| 84 | } |
| 85 | |
| 86 | const body: ProblemDetails = { |
| 87 | type, |
| 88 | title, |
| 89 | status, |
| 90 | instance, |
| 91 | detail: options.detail ?? undefined, |
| 92 | }; |
| 93 | const validated = ProblemDetailsSchema.safeParse(body); |
| 94 | if (!validated.success) { |
| 95 | log().error( |
| 96 | { |
| 97 | event: 'api.error.malformed-envelope', |
| 98 | issues: validated.error.issues, |
| 99 | body, |
| 100 | handler: options.handler, |
| 101 | originalStatus: status, |
| 102 | }, |
| 103 | 'errorResponse produced an invalid ProblemDetails body — emitting fallback', |
| 104 | ); |
| 105 | const fallbackStatus = 500 as const; |
| 106 | apiErrorCounter().add(1, { |
| 107 | type: 'urn:ok:error:internal-server-error', |
| 108 | ...(options.handler ? { handler: options.handler } : {}), |
| 109 | }); |
| 110 | res.writeHead(fallbackStatus, { |
| 111 | 'Content-Type': 'application/problem+json', |
| 112 | 'X-Content-Type-Options': 'nosniff', |
| 113 | }); |
| 114 | res.end( |
| 115 | JSON.stringify({ |
| 116 | type: 'urn:ok:error:internal-server-error' satisfies ProblemType, |
no test coverage detected