moeru-ai/airi · error · Error
Invalid message example format: ${line}
Error message
Invalid message example format: ${line} What it means
newAiriCard parses SillyTavern V3 character cards (ccv3): mes_example is split on the literal '<START>\n' and every remaining line must start with '{{char}}:' or '{{user}}:'. Any other line — a blank line, narration text, or a block produced by a '<START>' separator with different whitespace or line endings — throws with the offending line in the message. This makes loosely-authored real-world cards fail import outright.
Source
Thrown at packages/stage-ui/src/stores/modules/airi-card.ts:424
notesMultilingual: ccv3Card.data.creator_notes_multilingual,
personality: ccv3Card.data.personality ?? '',
scenario: ccv3Card.data.scenario ?? '',
greetings: [
ccv3Card.data.first_mes,
...(ccv3Card.data.alternate_greetings ?? []),
],
greetingsGroupOnly: ccv3Card.data.group_only_greetings ?? [],
systemPrompt: ccv3Card.data.system_prompt ?? '',
postHistoryInstructions: ccv3Card.data.post_history_instructions ?? '',
messageExample: ccv3Card.data.mes_example
? ccv3Card.data.mes_example
.split('<START>\n')
.filter(Boolean)
.map(example => example.split('\n')
.map((line) => {
if (line.startsWith('{{char}}:') || line.startsWith('{{user}}:'))
return line as `{{char}}: ${string}` | `{{user}}: ${string}`
throw new Error(`Invalid message example format: ${line}`)
}))
: [],
tags: ccv3Card.data.tags ?? [],
extensions: {
...ccv3Card.data.extensions,
airi: resolveAiriExtension(ccv3Card),
},
}
}
return {
...card,
extensions: {
...card.extensions,
airi: resolveAiriExtension(card),
},
}
}View on GitHub (pinned to f679616c34)
Solutions
- Pre-normalize mes_example before import: split on /<START>\r?\n/, trim lines, and drop or fix lines that lack the {{char}}:/{{user}}: prefix.
- Edit the card JSON directly and make every line inside mes_example start with '{{char}}:' or '{{user}}:'.
- Wrap the import in try/catch and show the offending line (already included in the error message) to the user.
- File an issue or relax the parser upstream (skip/blank-tolerant parsing) if strictness is not a product requirement.
Example fix
// before
const card = newAiriCard(ccv3)
// after - normalize before parsing so strict line rules pass
function normalizeMesExample(raw: string | undefined): string | undefined {
if (!raw) return undefined
const cleaned = raw
.split(/<START>\r?\n/)
.filter(Boolean)
.map(block => block
.split(/\r?\n/)
.filter(line => line.startsWith('{{char}}:') || line.startsWith('{{user}}:'))
.join('\n'))
.filter(Boolean)
.join('<START>\n')
return cleaned || undefined
}
const card = newAiriCard({
...ccv3,
data: { ...ccv3.data, mes_example: normalizeMesExample(ccv3.data.mes_example) },
}) Defensive patterns
Strategy: validation
Validate before calling
function hasValidMesExample(mesExample: string | undefined): boolean {
if (!mesExample) return true // absent is fine
return mesExample
.split('<START>\n')
.filter(Boolean)
.every(block => block
.split('\n')
.every(line => line.startsWith('{{char}}:') || line.startsWith('{{user}}:')))
}
if (!hasValidMesExample(ccv3.data.mes_example))
ccv3 = normalizeCardBeforeImport(ccv3) // strip/skip offending lines
const card = newAiriCard(ccv3) Type guard
function isParsableMesExampleLine(line: string): line is `{{char}}: ${string}` | `{{user}}: ${string}` {
return line.startsWith('{{char}}:') || line.startsWith('{{user}}:')
} Try / catch
try {
const card = newAiriCard(ccv3)
}
catch (error) {
const message = errorMessageFrom(error) ?? ''
if (message.startsWith('Invalid message example format:')) {
// the offending line is embedded in the message; show it for repair
showImportError(`Fix the example dialog line: ${message.split(': ').slice(1).join(': ')}`)
return
}
throw error
} Prevention
- Pre-validate mes_example line prefixes before import; normalize CRLF and blank lines.
- When authoring cards, keep every example line prefixed with {{char}}: or {{user}}:.
- Use the exact '<START>\n' separator between example blocks.
- Report the offending line (already in the error) instead of a generic import failure.
When it happens
Trigger: Importing a card whose example dialogs contain blank lines between turns or narration lines; mes_example using CRLF so the split on '<START>\n' leaves stray '\r' or unmatched separators; examples separated by '<START>' without the required trailing newline; hand-edited or third-party tool exports with free-form text inside examples.
Common situations: Downloading community character cards that render fine in SillyTavern but contain formatting the strict parser rejects; authors pasting dialogue with empty lines; cards authored on Windows editors inserting CRLF.
Related errors
- Invalid Character Card V3.
- The file does not contain valid JSON.
- The motion project source is invalid.
- The motion project source timeline is invalid.
- MiMo voice clone requires a base64 audio sample in data URI
AI-assisted analysis of moeru-ai/airi@f679616c34 (2026-08-18).
Data as JSON: /api/errors/727ef4608f6b829f.
Report an issue: GitHub.