{"record":{"id":"4fe09063add39858","repo":"moeru-ai/airi","slug":"invalid-character-card-v3","errorCode":null,"errorMessage":"Invalid Character Card V3.","messagePattern":"Invalid Character Card V3\\.","errorType":"validation","errorClass":"InvalidCharacterCardError","httpStatus":null,"severity":"error","filePath":"packages/ccc/src/codec/characterCardV3.ts","lineNumber":179,"sourceCode":"/** Checks whether an error came from the CCv3 parsing boundary. */\nexport function isInvalidCharacterCardError(error: unknown): error is InvalidCharacterCardError {\n  return error instanceof InvalidCharacterCardError\n}\n\n/**\n * Parses and validates a Character Card V3 object or JSON document.\n *\n * Unknown fields are preserved so a newer card can be inspected and exported\n * without silently discarding data AIRI does not understand yet. Older and\n * newer `spec_version` values are accepted and reported through\n * `compatibility`; callers can decide how prominently to warn users.\n */\nexport function parseCharacterCardV3(source: unknown): ParsedCharacterCardV3 {\n  const candidate = parseJsonSource(source)\n  const result = safeParse(characterCardV3Schema, candidate)\n\n  if (!result.success) {\n    throw new InvalidCharacterCardError({\n      cause: result.issues,\n      source,\n    })\n  }\n\n  return {\n    card: result.output,\n    compatibility: resolveCompatibility(result.output.spec_version),\n  }\n}\n\nfunction parseJsonSource(source: unknown): unknown {\n  if (typeof source !== 'string')\n    return source\n\n  try {\n    return JSON.parse(source)\n  }","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/ccc/src/codec/characterCardV3.ts#L161-L197","documentation":"parseCharacterCardV3 runs the Valibot characterCardV3Schema (via safeParse) over the decoded source and throws InvalidCharacterCardError when validation fails; the Valibot issues are attached as `cause` and the original input as `source`. Unknown fields are preserved and older/newer spec_version values are tolerated, so a throw indicates genuine structural problems such as missing or wrongly typed required fields.","triggerScenarios":"A card missing required fields (spec, spec_version, data.name), wrong field types (data.description not a string, character_book entries malformed), or a V2 card passed straight into the V3 parser without conversion.","commonSituations":"Importing cards exported by other frontends with subtle schema drift; hand-edited JSON that dropped a required key; upstream generators emitting null for optional-but-typed fields; truncated downloads.","solutions":["Catch the error and inspect `cause` — it holds the Valibot issues with exact paths (e.g. data.name missing)","Check the V3 contract: spec === 'chara_card_v3', spec_version string, and required data fields present with correct types","If the card is V2 (spec 'chara_card_v2'), convert/upgrade it to V3 before parsing","Fix the source and re-parse; unknown extra fields do not need removal"],"exampleFix":"// before\nconst { card } = parseCharacterCardV3(raw) // Error: Invalid Character Card V3.\n\n// after\nimport { parseCharacterCardV3, isInvalidCharacterCardError } from '@proj-airi/ccc'\ntry {\n  const { card } = parseCharacterCardV3(raw)\n}\ncatch (err) {\n  if (isInvalidCharacterCardError(err)) {\n    for (const issue of err.cause ?? [])\n      console.error(issue.path?.join('.'), issue.message)\n  }\n  throw err\n}","handlingStrategy":"type-guard","validationCode":"// Optional pre-check with the same schema machinery before committing to a parse\nimport { safeParse } from 'valibot'\nconst check = safeParse(characterCardV3Schema, typeof raw === 'string' ? JSON.parse(raw) : raw)\nif (!check.success) {\n  showIssues(check.issues) // field-level feedback before the throwing API\n}","typeGuard":"import { isInvalidCharacterCardError } from '@proj-airi/ccc'\n// isInvalidCharacterCardError(error): error is InvalidCharacterCardError\n// narrowing via instanceof, exposes .cause (Valibot issues) and .source","tryCatchPattern":"try {\n  const { card, compatibility } = parseCharacterCardV3(source)\n}\ncatch (err) {\n  if (isInvalidCharacterCardError(err)) {\n    // err.cause holds Valibot issues (schema failure) or a SyntaxError (JSON failure)\n    reportCardIssues(err.cause)\n    return\n  }\n  throw err\n}","preventionTips":["Validate at import boundaries and show issue paths to the user immediately","Convert V2 cards to V3 before parsing with the V3 codec","Round-trip exports through parseCharacterCardV3 in tests to catch schema drift"],"tags":["character-card","schema-validation","valibot","parsing"],"backgroundTag":"schema-validation-failed","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}