linshenkx/prompt-optimizer · error · RequestConfigError
Message content must be a string
Error message
Message content must be a string
What it means
Thrown by validateMessages when a message's content is present but is not a string (e.g. a number, object, or array). The adapter's request serialization expects plain string content; multimodal/content-part arrays are not supported by this validation path. It is a client-side RequestConfigError raised before any network request.
Source
Thrown at packages/core/src/services/llm/adapters/abstract-adapter.ts:197
if (!Array.isArray(messages)) {
throw new RequestConfigError('Messages must be an array')
}
if (messages.length === 0) {
throw new RequestConfigError('Messages array cannot be empty')
}
for (const msg of messages) {
if (!msg.role || !msg.content) {
throw new RequestConfigError('Each message must have role and content')
}
if (!['system', 'user', 'assistant', 'tool'].includes(msg.role)) {
throw new RequestConfigError(`Invalid message role: ${msg.role}`)
}
if (typeof msg.content !== 'string') {
throw new RequestConfigError('Message content must be a string')
}
}
}
protected validateImageUnderstandingRequest(request: ImageUnderstandingRequest): void {
if (!request || typeof request !== 'object') {
throw new RequestConfigError('Image understanding request cannot be empty')
}
if (typeof request.userPrompt !== 'string' || !request.userPrompt.trim()) {
throw new RequestConfigError('Image understanding user prompt cannot be empty')
}
if (!Array.isArray(request.images) || request.images.length === 0) {
throw new RequestConfigError('Image understanding request requires at least one image')
}
request.images.forEach((image, index) => {View on GitHub (pinned to 3e677b1d9f)
Solutions
- Stringify content before sending: String(value) or JSON.stringify for structured data
- For multimodal/image input, use the dedicated sendImageUnderstanding API instead of message content arrays
- Type messages strictly so content: string is enforced at compile time
Example fix
// before
messages = [{ role: 'user', content: [{ type: 'text', text: 'hi' }] }]
// after
messages = [{ role: 'user', content: 'hi' }] Defensive patterns
Strategy: validation
Validate before calling
messages.forEach(m => { if (typeof m.content !== 'string') m.content = String(m.content) }) Type guard
function hasStringContent(m: unknown): m is Message {
return !!m && typeof (m as Message).content === 'string'
} Try / catch
null
Prevention
- Keep Message.content strictly typed as string
- JSON.stringify structured payloads instead of passing objects
- Use sendImageUnderstanding for multimodal input, not message content
When it happens
Trigger: Passing content as a number (content: 42), a content-parts array (content: [{type:'text',...}]), or an object; JSON.parse results where content ended up as a non-string value.
Common situations: Reusing OpenAI multimodal content arrays; interpolating computed values into content without String(); loose typing where Message['content'] is any.
Related errors
- Messages array cannot be empty
- Each message must have role and content
- Invalid message role: ${msg.role}
- Image at index ${index} is invalid
- Model provider metadata cannot be empty
AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27).
Data as JSON: /api/errors/218f0af732bd608e.
Report an issue: GitHub.