From 81728723296eafe86e07d83416c1e6261df04409 Mon Sep 17 00:00:00 2001 From: CTO Date: Fri, 10 Apr 2026 07:43:43 +0000 Subject: [PATCH] fix: coerce empty FormData strings to null for UUID columns (AIIA-59) Document upload failed with "invalid input syntax for type uuid" when decision_id or norm_instrument_id were sent as empty strings from FormData. The ?? operator only catches null/undefined, not "". Added emptyToUndefined() to sanitize all optional UUID fields at the API boundary before they reach the DB insert. Co-Authored-By: Paperclip --- src/app/api/documents/route.ts | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/app/api/documents/route.ts b/src/app/api/documents/route.ts index 653c05e..7010d70 100644 --- a/src/app/api/documents/route.ts +++ b/src/app/api/documents/route.ts @@ -8,6 +8,12 @@ import { requirePermission } from '@/lib/auth/rbac'; const VALID_CATEGORIES = new Set(['entscheidung', 'norm', 'falldokument', 'sonstiges']); +/** Convert empty/whitespace-only strings to undefined (FormData sends "" for blank fields). */ +function emptyToUndefined(value: string | null): string | undefined { + if (!value || value.trim() === '') return undefined; + return value; +} + export async function POST(request: NextRequest) { const auth = await requirePermission('cases:edit'); if ('response' in auth) return auth.response; @@ -16,9 +22,9 @@ export async function POST(request: NextRequest) { const formData = await request.formData(); const file = formData.get('file'); const category = (formData.get('category') as string) || 'sonstiges'; - const caseId = formData.get('caseId') as string | null; - const decisionId = formData.get('decisionId') as string | null; - const normInstrumentId = formData.get('normInstrumentId') as string | null; + const caseId = emptyToUndefined(formData.get('caseId') as string | null); + const decisionId = emptyToUndefined(formData.get('decisionId') as string | null); + const normInstrumentId = emptyToUndefined(formData.get('normInstrumentId') as string | null); if (!file || !(file instanceof File)) { return Response.json( @@ -44,9 +50,9 @@ export async function POST(request: NextRequest) { userId: ctx.userId, file, category: category as 'entscheidung' | 'norm' | 'falldokument' | 'sonstiges', - caseId: caseId ?? undefined, - decisionId: decisionId ?? undefined, - normInstrumentId: normInstrumentId ?? undefined, + caseId, + decisionId, + normInstrumentId, }); await logAuditEvent( @@ -75,9 +81,9 @@ export async function GET(request: NextRequest) { const limit = Math.min(parseInt(searchParams.get('limit') ?? '20', 10), 100); const offset = parseInt(searchParams.get('offset') ?? '0', 10); const category = searchParams.get('category') as string | null; - const caseId = searchParams.get('caseId') as string | null; - const decisionId = searchParams.get('decisionId') as string | null; - const normInstrumentId = searchParams.get('normInstrumentId') as string | null; + const caseId = emptyToUndefined(searchParams.get('caseId')); + const decisionId = emptyToUndefined(searchParams.get('decisionId')); + const normInstrumentId = emptyToUndefined(searchParams.get('normInstrumentId')); const docs = await listDocuments( ctx.tenantId, @@ -85,9 +91,9 @@ export async function GET(request: NextRequest) { category: category && VALID_CATEGORIES.has(category) ? category as 'entscheidung' | 'norm' | 'falldokument' | 'sonstiges' : undefined, - caseId: caseId ?? undefined, - decisionId: decisionId ?? undefined, - normInstrumentId: normInstrumentId ?? undefined, + caseId, + decisionId, + normInstrumentId, }, limit, offset,