linshenkx/prompt-optimizer · error · CompareValidationError
Original text must be a string
Error message
Original text must be a string
What it means
Thrown by buildReferencePromptPrompts when the configured reference image template's messages contain no message with role 'user'. The extractor builds its model call from a template, and a user prompt is mandatory for the request. A template with only system/assistant messages, or a mismatched role string, triggers this.
Source
Thrown at packages/core/src/services/compare/service.ts:66
};
} catch (error) {
if (error instanceof CompareValidationError) {
throw error;
}
const errorMessage = error instanceof Error ? error.message : String(error);
throw new CompareCalculationError(
`Text comparison calculation failed: ${errorMessage}`
);
}
}
/**
* 验证输入参数
*/
private validateInput(original: string, optimized: string): void {
if (typeof original !== 'string') {
throw new CompareValidationError('Original text must be a string');
}
if (typeof optimized !== 'string') {
throw new CompareValidationError('Optimized text must be a string');
}
}
/**
* 执行文本对比 - 使用 jsdiff
*/
private performTextComparison(
original: string,
optimized: string,
options: CompareOptions
): TextFragment[] {
let diffResult: Change[];
// 根据配置处理文本预处理
let processedOriginal = original;View on GitHub (pinned to 3e677b1d9f)
Solutions
- Ensure the template's messages include at least one entry with role exactly 'user' containing the prompt text
- Validate templates at save/import time with a schema check requiring a user-role message
- Check for role casing/whitespace issues — normalize roles before collectPromptByRole
Example fix
// before
const template = { messages: [{ role: 'system', content: 'You extract styles.' }] }
// after
const template = {
messages: [
{ role: 'system', content: 'You extract styles.' },
{ role: 'user', content: 'Extract the visual style from this reference image: {{image}}' },
],
} Defensive patterns
Strategy: validation
Validate before calling
function templateHasUserPrompt(template: { messages: Array<{ role: string; content?: string }> }): boolean {
return template.messages.some(
(m) => m.role?.toLowerCase() === 'user' && typeof m.content === 'string' && m.content.length > 0,
)
}
if (!templateHasUserPrompt(template)) {
throw new Error('Template must contain a user prompt before running extraction')
} Type guard
type ChatMessage = { role: string; content?: string }
function isUserMessage(m: ChatMessage): m is ChatMessage & { role: 'user'; content: string } {
return m.role === 'user' && typeof m.content === 'string' && m.content.length > 0
} Try / catch
try {
const prompts = buildReferencePromptPrompts(template)
} catch (error) {
if (error instanceof Error && error.message === 'Reference image template is missing a user prompt') {
return fixTemplateByAddingDefaultUserMessage(template)
}
throw error
} Prevention
- Validate templates at import/save time, not at call time
- Normalize message roles to lowercase when loading templates
- Provide a default user prompt in the editor when the user clears it
When it happens
Trigger: Calling prompts / buildReferencePromptPrompts with a reference image template whose messages array has no role==='user' entry (missing, typo'd role like 'User', or empty messages).
Common situations: Custom user-supplied templates; template schema migration where 'user' role was renamed; template loaded from storage/JSON where the user message was dropped; localization tools altering role fields.
AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27).
Data as JSON: /api/errors/b6514bbc3e62babf.
Report an issue: GitHub.