{"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/27111382b4a79a7e983289d6e983a06af185ed0f/packages/ccc/src/codec/characterCardV3.ts#L161-L197","documentation":"Thrown as InvalidCharacterCardError by parseCharacterCardV3() when valibot's safeParse against characterCardV3Schema fails. The schema enforces spec === 'chara_card_v3', a dotted spec_version, and a nested data object with required string fields; failure means the input is structurally not a CCv3 card. The error's cause carries the valibot issues and .source carries the original input.","triggerScenarios":"Passing an object or JSON string that is missing required fields (e.g. no data.name, data.first_mes), has spec set to 'chara_card_v2', uses wrong types (alternate_greetings not a string array), or a spec_version that does not match /^\\d+(?:\\.\\d+)*$/.","commonSituations":"Loading a Character Card V2 file into a V3-only parser; user-uploaded card missing required fields; hand-edited JSON with a typo in spec or spec_version; a card exported by a tool that omits empty strings; downstream code that assumes any JSON object is a valid card.","solutions":["Inspect error.cause (valibot issues) to see the exact failing path and expected type.","Upgrade V2 cards to V3: set spec='chara_card_v3', spec_version='3.0', and nest fields under data.","If you must accept older cards, detect the version first and run the V2/V1 parser instead of parseCharacterCardV3.","Validate with a JSON schema tool upstream and surface field-level errors to the user."],"exampleFix":"// before\nconst parsed = parseCharacterCardV3(cardJsonV2) // { spec: 'chara_card_v2', name: ... }\n// after\nconst v3 = { spec: 'chara_card_v3', spec_version: '3.0', data: { ...cardJsonV2, alternate_greetings: [], group_only_greetings: [], tags: [], ... } }\nconst parsed = parseCharacterCardV3(v3)","handlingStrategy":"try-catch","validationCode":"import { safeParse } from 'valibot'\n// Validate shape upstream with the same schema exports if available, else a lightweight check:\nfunction looksLikeV3(card: unknown): boolean {\n  return !!card && typeof card === 'object' && (card as any).spec === 'chara_card_v3' && !!(card as any).data\n}","typeGuard":"import { isInvalidCharacterCardError } from '@proj-airi/ccc'\n// isInvalidCharacterCardError narrows the thrown error type after catch\nfunction isCardLike(v: unknown): v is { spec: unknown; data: unknown } {\n  return !!v && typeof v === 'object' && 'spec' in v && 'data' in v\n}","tryCatchPattern":"import { isInvalidCharacterCardError, parseCharacterCardV3 } from '@proj-airi/ccc'\n\ntry {\n  const parsed = parseCharacterCardV3(input)\n} catch (error) {\n  if (isInvalidCharacterCardError(error)) {\n    console.error('Card rejected. Issues:', error.cause)\n    // surface error.cause (valibot issues) to the user\n  }\n  throw error\n}","preventionTips":["Use isInvalidCharacterCardError() to branch on parse failures distinctly.","Detect the spec version before choosing V1/V2/V3 parser.","Show error.cause (valibot issues) to users for actionable field-level fixes."],"tags":["character-card","valibot","schema-validation","json-parsing"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}