{"record":{"id":"c0855d4626d1adc2","repo":"thedotmack/claude-mem","slug":"validationerror-c0855d","errorCode":"ValidationError","errorMessage":"ValidationError","messagePattern":"ValidationError","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"src/services/worker/http/middleware/validateBody.ts","lineNumber":9,"sourceCode":"\nimport type { RequestHandler } from 'express';\nimport type { ZodTypeAny } from 'zod';\n\nexport const validateBody = <S extends ZodTypeAny>(schema: S): RequestHandler =>\n  (req, res, next) => {\n    const result = schema.safeParse(req.body);\n    if (!result.success) {\n      res.status(400).json({\n        error: 'ValidationError',\n        issues: result.error.issues.map(i => ({\n          path: i.path,\n          message: i.message,\n          code: i.code,\n        })),\n      });\n      return;\n    }\n    req.body = result.data;\n    next();\n  };\n","sourceCodeStart":1,"sourceCodeEnd":22,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/worker/http/middleware/validateBody.ts#L1-L22","documentation":"HTTP 400 produced by the validateBody(schema) Express middleware when the JSON body of a POST route fails its Zod schema. The response is machine-readable: { error: 'ValidationError', issues: [{ path, message, code }] }, one entry per failed check. On success the parsed (and coerced/defaulted) value replaces req.body.","triggerScenarios":"POSTing to schema-validated routes — /api/corpus (buildCorpusSchema), /api/corpus/:name/query (queryCorpusSchema) — with a wrong type (string where number expected), an unknown enum value, a missing required field such as query, or an exceeded limit.","commonSituations":"Client and worker built from different versions so the schema drifted; passing date strings in the wrong format for date_start/date_end; sending limit as a string because it came from CLI argv without Number() conversion; extra whitespace/BOM breaking JSON parsing upstream so body is a string.","solutions":["Inspect response.issues[] — each states the exact path, Zod code and human message","Fix the offending field to match the exported schema (buildCorpusSchema / queryCorpusSchema)","Validate client-side with the same Zod schema before sending to get errors at build time"],"exampleFix":"// before\nawait fetch(`${base}/api/corpus`, {\n  method: 'POST',\n  body: JSON.stringify({ name: 'x', limit: '50' }), // 400: limit must be number\n});\n\n// after\nawait fetch(`${base}/api/corpus`, {\n  method: 'POST',\n  body: JSON.stringify({ name: 'x', limit: 50 }),\n});","handlingStrategy":"validation","validationCode":"import { z } from 'zod';\nconst payloadSchema = z.object({\n  name: z.string().min(1),\n  limit: z.number().int().positive().max(1000).optional(),\n  date_start: z.string().date().optional(),\n});\nconst safe = payloadSchema.parse(raw); // throws before the request is even sent","typeGuard":"interface ValidationIssues {\n  error: 'ValidationError';\n  issues: Array<{ path: (string | number)[]; message: string; code: string }>;\n}\nfunction isValidationIssues(body: unknown): body is ValidationIssues {\n  return typeof body === 'object' && body !== null &&\n    (body as { error?: unknown }).error === 'ValidationError' &&\n    Array.isArray((body as { issues?: unknown }).issues);\n}","tryCatchPattern":"const res = await callApi();\nif (res.status === 400) {\n  const body = await res.json();\n  if (isValidationIssues(body))\n    throw new Error(body.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; '));\n}","preventionTips":["Share the worker's Zod schemas with the client and parse payloads locally first","Coerce CLI string args with Number() before putting them into JSON bodies","Write a failing-shape test per validated endpoint so schema drift is caught in CI"],"tags":["http-400","zod","validation","schema","express-middleware"],"backgroundTag":"schema-validation-failed","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}