{"record":{"id":"964ae61779d1a7a9","repo":"JuliusBrussee/caveman","slug":"middleware-field-ajv-errorstext-validate-errors","errorCode":null,"errorMessage":"middleware ${field}: ${ajv.errorsText(validate?.errors)}","messagePattern":"middleware (.+?): (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/shared/contracts/scripts/validate-schemas.mjs","lineNumber":88,"sourceCode":"  \"recovery_handles\",\n  \"accounting_method\",\n  \"failure_fallback\",\n]) {\n  if (JSON.stringify(claude[key]) !== JSON.stringify(pi[key])) {\n    throw new Error(`agent conformance mismatch: ${key}`);\n  }\n}\nif (claude.build_sha256 === pi.build_sha256) {\n  throw new Error(\"adapter-specific build_sha256 values must differ\");\n}\nif (claude.failure_fallback !== \"original\") {\n  throw new Error(\"unknown adapter failure must preserve original bytes\");\n}\n\nconst middleware = JSON.parse(await readFile(path.join(packageRoot, \"..\", \"..\", \"sdk\", \"parity\", \"middleware.fixtures.json\"), \"utf8\"));\nfor (const [field, schemaName] of Object.entries({ capabilities: \"capabilities\", request: \"optimize\", plan: \"plan\", page: \"page\" })) {\n  const validate = ajv.getSchema(`https://caveman.so/schemas/middleware-${schemaName}.schema.json`);\n  if (!validate?.(middleware[field])) throw new Error(`middleware ${field}: ${ajv.errorsText(validate?.errors)}`);\n}\n\nconsole.log(\n  `validated ${files.length} JSON schemas and ${fixtureFiles.length} static agent contract fixtures (not executable parity)`,\n);\n","sourceCodeStart":70,"sourceCodeEnd":94,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/shared/contracts/scripts/validate-schemas.mjs#L70-L94","documentation":"The validator loads sdk/parity/middleware.fixtures.json and validates its `capabilities`, `request` (against the optimize schema), `plan`, and `page` fields against the compiled middleware-*.schema.json schemas via AJV. If a field is missing from the fixture, fails its schema, or the named schema was not compiled/registered (validate is undefined), it throws `middleware <field>: <errorsText>` (or \"undefined\" when validate is undefined). This keeps the SDK middleware parity fixtures aligned with the wire schemas.","triggerScenarios":"Running the contracts validation script when: middleware.fixtures.json is missing one of the four fields or misspells it (e.g. `resquest`); a fixture value violates its schema (wrong type, unknown enum, additionalProperties not allowed, missing required key); a middleware-*.schema.json file was renamed, its $id changed, or failed to compile so ajv.getSchema returns undefined; the fixture file itself is missing (readFile throws first) or malformed (JSON.parse throws first).","commonSituations":"Editing middleware.fixtures.json by hand for a new capability and adding a field the schema forbids; a schema $id rename in packages/shared/contracts/schemas that breaks the getSchema URL lookup; regenerating the fixture from a service whose output evolved past the schema; a merge that took the new fixture with the old schemas (or vice versa).","solutions":["Read the error text: the field name tells you which fixture section failed, and errorsText lists the exact AJV violations (path, keyword, expected value).","Fix the offending value in sdk/parity/middleware.fixtures.json to satisfy packages/shared/contracts/schemas/middleware-<name>.schema.json.","If validate?.errors is undefined, the schema lookup failed: verify the schema file exists, its $id matches https://caveman.so/schemas/middleware-<schemaName>.schema.json, and it compiles.","If the fixture legitimately models new behavior, update the schema first (bump version if it's a breaking wire change), then regenerate the fixture.","Ensure schemas and fixtures are updated in the same commit so they cannot drift."],"exampleFix":"// before (middleware.fixtures.json)\n\"plan\": { \"transformIds\": [\"caveman.cache-preserve.v1\"] }\n\n// after (schema uses snake_case ids)\n\"plan\": { \"ordered_transform_ids\": [\"caveman.cache-preserve.v1\"] }","handlingStrategy":"validation","validationCode":"import Ajv2020 from \"ajv/dist/2020.js\";\nconst ajv = new Ajv2020({ allErrors: true, strict: true });\najv.addSchema(schemas);\nconst middleware = JSON.parse(await readFile(\"sdk/parity/middleware.fixtures.json\", \"utf8\"));\nfor (const [field, name] of Object.entries({ capabilities: \"capabilities\", request: \"optimize\", plan: \"plan\", page: \"page\" })) {\n  const v = ajv.getSchema(`https://caveman.so/schemas/middleware-${name}.schema.json`);\n  if (!v) throw new Error(`missing compiled schema for ${field}`);\n  if (!v(middleware[field])) throw new Error(`${field}: ${ajv.errorsText(v.errors)}`);\n}","typeGuard":"function hasMiddlewareFields(mw) {\n  return typeof mw === \"object\" && mw !== null &&\n    [\"capabilities\", \"request\", \"plan\", \"page\"].every(k => k in mw);\n}","tryCatchPattern":"try {\n  await runValidationScript();\n} catch (err) {\n  const m = err.message.match(/^middleware (\\w+): (.+)$/);\n  if (m) {\n    console.error(`Fixture field '${m[1]}' failed schema: ${m[2]}`);\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["Regenerate middleware.fixtures.json from the service rather than editing by hand.","When changing a middleware schema's $id or shape, update the fixture and the getSchema lookup URL in validate-schemas.mjs in the same commit.","Run the contracts validation script locally before pushing so AJV errors surface early with field-level detail.","Keep schemas strict (additionalProperties: false) so drift fails fast with a precise path."],"tags":["build","schema-validation","ajv","fixtures"],"backgroundTag":"schema-validation-failed","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}