moeru-ai/airi · error · InvalidCharacterCardError
Invalid Character Card V3.
Error message
Invalid Character Card V3.
What it means
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.
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
Example fix
// before
const { card } = parseCharacterCardV3(raw) // Error: Invalid Character Card V3.
// after
import { parseCharacterCardV3, isInvalidCharacterCardError } from '@proj-airi/ccc'
try {
const { card } = parseCharacterCardV3(raw)
}
catch (err) {
if (isInvalidCharacterCardError(err)) {
for (const issue of err.cause ?? [])
console.error(issue.path?.join('.'), issue.message)
}
throw err
} Defensive patterns
Strategy: type-guard
Validate before calling
// Optional pre-check with the same schema machinery before committing to a parse
import { safeParse } from 'valibot'
const check = safeParse(characterCardV3Schema, typeof raw === 'string' ? JSON.parse(raw) : raw)
if (!check.success) {
showIssues(check.issues) // field-level feedback before the throwing API
} Type guard
import { isInvalidCharacterCardError } from '@proj-airi/ccc'
// isInvalidCharacterCardError(error): error is InvalidCharacterCardError
// narrowing via instanceof, exposes .cause (Valibot issues) and .source Try / catch
try {
const { card, compatibility } = parseCharacterCardV3(source)
}
catch (err) {
if (isInvalidCharacterCardError(err)) {
// err.cause holds Valibot issues (schema failure) or a SyntaxError (JSON failure)
reportCardIssues(err.cause)
return
}
throw err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid Godot stage view-state error payload.
- Invalid Godot stage view-state patch payload.
- Invalid Godot stage view-state snapshot payload.
- Invalid message example format
- The file is not an AIRI Live2D motion project.
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/4fe09063add39858.
Report an issue: GitHub.
Appendix: source
Thrown at packages/ccc/src/codec/characterCardV3.ts:179
/** Checks whether an error came from the CCv3 parsing boundary. */
export function isInvalidCharacterCardError(error: unknown): error is InvalidCharacterCardError {
return error instanceof InvalidCharacterCardError
}
/**
* Parses and validates a Character Card V3 object or JSON document.
*
* Unknown fields are preserved so a newer card can be inspected and exported
* without silently discarding data AIRI does not understand yet. Older and
* newer `spec_version` values are accepted and reported through
* `compatibility`; callers can decide how prominently to warn users.
*/
export function parseCharacterCardV3(source: unknown): ParsedCharacterCardV3 {
const candidate = parseJsonSource(source)
const result = safeParse(characterCardV3Schema, candidate)
if (!result.success) {
throw new InvalidCharacterCardError({
cause: result.issues,
source,
})
}
return {
card: result.output,
compatibility: resolveCompatibility(result.output.spec_version),
}
}
function parseJsonSource(source: unknown): unknown {
if (typeof source !== 'string')
return source
try {
return JSON.parse(source)
}View on GitHub (pinned to 677329427f)