From 38c78c0bb5c68877fbdbd251e9b4d6acb213f211 Mon Sep 17 00:00:00 2001 From: Mitchell R Date: Tue, 26 May 2026 15:15:41 +0200 Subject: [PATCH] fix: log validation errors with field detail + raw body on event reject validateBody now extracts per-field error messages from anyvali issues. Event endpoint logs the raw body (first 500 chars) on validation failure so we can see exactly what the kiosk sends. Co-Authored-By: Claude Opus 4.6 (1M context) --- server/src/plugins/service-api-http/index.ts | 12 +++++++++++- server/src/shared/api-schemas.ts | 10 +++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/server/src/plugins/service-api-http/index.ts b/server/src/plugins/service-api-http/index.ts index 969fbb7..4fb3a87 100644 --- a/server/src/plugins/service-api-http/index.ts +++ b/server/src/plugins/service-api-http/index.ts @@ -626,7 +626,17 @@ function registerKioskRoutes( const kiosk = await auth.verifyKioskKey(token); if (!kiosk) throw createError({ statusCode: 401, statusMessage: "Invalid kiosk key" }); - const body = validateBody(EventBody, await readBody(event)); + const raw = await readBody(event); + let body: ReturnType; + try { + body = validateBody(EventBody, raw); + } catch (err: any) { + event.context.obs?.log.warn("event validation failed: {msg} body={raw}", { + msg: err.message ?? "unknown", + raw: JSON.stringify(raw).slice(0, 500), + }); + throw err; + } const payload = (body.payload ?? {}) as Record; event.context.obs?.log.info("event from kiosk {id} topic {topic}", { id: String(kiosk.id), topic: body.topic }); diff --git a/server/src/shared/api-schemas.ts b/server/src/shared/api-schemas.ts index dc63f68..d96250c 100644 --- a/server/src/shared/api-schemas.ts +++ b/server/src/shared/api-schemas.ts @@ -155,9 +155,13 @@ export const PasswordChangeBody = av.object( export function validateBody(schema: { safeParse(input: unknown): { success: boolean; data?: T; error?: unknown } }, raw: unknown): T { const result = schema.safeParse(raw); if (!result.success) { - const msg = typeof result.error === "object" && result.error && "message" in result.error - ? String((result.error as any).message) - : "invalid request body"; + let msg = "invalid request body"; + const err = result.error as any; + if (err?.issues) { + msg = err.issues.map((i: any) => `${i.path?.join?.(".") ?? "?"}: ${i.message}`).join("; "); + } else if (err?.message) { + msg = String(err.message); + } throw Object.assign(new Error(msg), { status: 400, statusText: "Bad Request" }); } return result.data as T;