Spaces:
Sleeping
Sleeping
File size: 3,194 Bytes
27329ef 5bbf908 27329ef a227722 f7a6136 5bbf908 27329ef a227722 f7a6136 27329ef | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | import { errorEnvelopeSchema } from "@oloka/contracts";
import type { FastifyInstance } from "fastify";
import { ZodError } from "zod";
import { ApplicationError, publicErrorCatalog } from "./application-error.js";
import { readMaterializationStage } from "../composition/composition-materializer.js";
export function registerErrorHandler(app: FastifyInstance): void {
app.setErrorHandler((error, request, reply) => {
const applicationError = normalizeApplicationError(error);
const definition = publicErrorCatalog[applicationError.code];
const requestId = request.id;
reply.header("x-request-id", requestId);
for (const [name, value] of Object.entries(
applicationError.responseHeaders ?? {},
)) {
reply.header(name, value);
}
if (applicationError.code === "INTERNAL_ERROR") {
request.log.error(
{
event: "http.request.failed",
requestId,
internalCause: applicationError.internalCause ?? "unexpected_error",
errorType: error instanceof Error ? error.name : "UnknownError",
errorCode: readErrorCode(error),
errorSyscall: readErrorSyscall(error),
previewStage: readPreviewStage(error),
materializationStage: readMaterializationStage(error),
},
"request failed",
);
}
return reply.code(definition.statusCode).send(
errorEnvelopeSchema.parse({
error: {
code: applicationError.code,
retryable: definition.retryable,
messageKey: definition.messageKey,
suggestedAction: definition.suggestedAction,
requestId,
},
}),
);
});
}
function readErrorCode(error: unknown): string | undefined {
if (typeof error !== "object" || error === null) return undefined;
const code = (error as { code?: unknown }).code;
return typeof code === "string" ? code : undefined;
}
function readErrorSyscall(error: unknown): string | undefined {
if (typeof error !== "object" || error === null) return undefined;
const syscall = (error as { syscall?: unknown }).syscall;
return typeof syscall === "string" ? syscall : undefined;
}
function readPreviewStage(error: unknown): string | undefined {
if (typeof error !== "object" || error === null) return undefined;
const stage = (error as { previewStage?: unknown }).previewStage;
return typeof stage === "string" ? stage : undefined;
}
function normalizeApplicationError(error: unknown): ApplicationError {
if (error instanceof ApplicationError) return error;
if (error instanceof ZodError) {
return new ApplicationError("VALIDATION_ERROR", "request_schema_invalid");
}
if (isFastifyValidationError(error)) {
return new ApplicationError(
"VALIDATION_ERROR",
"fastify_validation_failed",
);
}
return new ApplicationError("INTERNAL_ERROR", "unexpected_application_error");
}
function isFastifyValidationError(error: unknown): boolean {
if (typeof error !== "object" || error === null) return false;
const candidate = error as { statusCode?: unknown; code?: unknown };
return (
candidate.statusCode === 400 || candidate.code === "FST_ERR_VALIDATION"
);
}
|