mastra-ai/mastra · error · MastraError
INVALID_SYSTEM_MESSAGE_FORMAT
INVALID_SYSTEM_MESSAGE_FORMAT
Error message
Invalid system message format. System messages must be CoreMessage format with 'role' and 'content' properties. The content should be a string or valid content array.
What it means
MessageList.add() validates every system-role message against known message shapes (AI SDK v4/v5/v6 CoreMessage, or MastraDBMessage). If the message has role 'system' but none of the type detectors recognize its shape, the list refuses to store it and throws INVALID_SYSTEM_MESSAGE_FORMAT. This guards the internal prompt pipeline, which assumes system messages conform to the CoreMessage contract ('role' plus string or content-array 'content').
Source
Thrown at packages/core/src/agent/message-list/message-list.ts:1679
}
if (message.role === `system`) {
// In the past system messages were accidentally stored in the db. these should be ignored because memory is not supposed to store system messages.
if (messageSource === `memory`) return null;
// Check if the message is in a supported format for system messages
const isSupportedSystemFormat =
TypeDetector.isAIV4CoreMessage(message) ||
TypeDetector.isAIV6CoreMessage(message) ||
TypeDetector.isAIV5CoreMessage(message) ||
TypeDetector.isMastraDBMessage(message);
if (isSupportedSystemFormat) {
return this.addSystem(message);
}
// if we didn't add the message and we didn't ignore this intentionally, then it's a problem!
throw new MastraError({
id: 'INVALID_SYSTEM_MESSAGE_FORMAT',
domain: ErrorDomain.AGENT,
category: ErrorCategory.USER,
text: `Invalid system message format. System messages must be CoreMessage format with 'role' and 'content' properties. The content should be a string or valid content array.`,
details: {
messageSource,
receivedMessage: JSON.stringify(message, null, 2),
},
});
}
const messageV2 = convertInputToMastraDBMessage(message, messageSource, this.createAdapterContext());
const signalMetadata =
messageV2.role === 'signal'
? (messageV2.content.metadata?.signal as { acceptedAt?: string; createdAt?: string } | undefined)
: undefined;
if (messageSource === 'input' && messageV2.role === 'signal' && !signalMetadata?.acceptedAt) {
const acceptedAt = signalMetadata?.createdAt ?? messageV2.createdAt.toISOString();View on GitHub (pinned to 75dd419e61)
Solutions
- Ensure the system message is a plain CoreMessage: { role: 'system', content: 'your instructions' } with content as a string or a valid content-part array.
- Check the receivedMessage in error details to see what shape actually arrived and fix the producer that serialized it.
- If migrating from an older Mastra/AI SDK version, convert stored messages to the current CoreMessage/DBMessage format before adding them.
- If passing pre-built message arrays to an agent, use the types from @mastra/core (CoreMessage / MastraDBMessage) instead of ad-hoc objects.
Example fix
// before
messages.push({ role: 'system', content: { text: 'You are helpful' } } as any);
// after
messages.push({ role: 'system', content: 'You are helpful' }); Defensive patterns
Strategy: type-guard
Validate before calling
function isValidSystemMessage(m: unknown): boolean {
return (
!!m && typeof m === 'object' && (m as any).role === 'system' &&
(typeof (m as any).content === 'string' ||
(Array.isArray((m as any).content) && (m as any).content.length > 0))
);
}
if (!isValidSystemMessage(msg)) throw new Error('bad system message'); Type guard
function isCoreSystemMessage(m: unknown): m is { role: 'system'; content: string | Array<{ type: string; [k: string]: unknown }> } {
return typeof m === 'object' && m !== null &&
(m as any).role === 'system' &&
(typeof (m as any).content === 'string' ||
(Array.isArray((m as any).content) && (m as any).content.every((p: unknown) => typeof p === 'object' && p !== null)));
} Try / catch
try {
messageList.add(msg, 'memory');
} catch (e) {
if (e instanceof MastraError && e.id === 'INVALID_SYSTEM_MESSAGE_FORMAT') {
console.error('System message rejected:', e.detail?.receivedMessage);
return; // skip or re-serialize to CoreMessage
}
throw e;
} Prevention
- Construct system messages only via typed helpers (CoreMessage types from the AI SDK).
- Never hand-serialize system messages through custom storage without re-validating on read.
- Add a unit test asserting persisted messages round-trip through MessageList.add.
When it happens
Trigger: Calling messageList.add({ role: 'system', ... }) or agent methods that ingest memory/input messages with a system message whose shape fails TypeDetector.isAIV4CoreMessage / isAIV5CoreMessage / isAIV6CoreMessage / isMastraDBMessage — e.g. content is an object, undefined, or an unexpected array element shape, or the message uses a custom/legacy message class.
Common situations: Hand-rolled message objects from custom storage layers or ORMs that serialize content differently; mixing Mastra Memory output with older AI SDK message versions; loading messages persisted by an older Mastra version into a new MessageList; typos like 'roles' instead of 'role'.
Related errors
- INVALID_SYSTEM_MESSAGE_FORMAT
- AGENT_REQUEST_CONTEXT_VALIDATION_FAILED
- Either message or messages must be provided
- AGENT_LIST_SUSPENDED_RUNS_INVALID_PER_PAGE
- AGENT_LIST_SUSPENDED_RUNS_INVALID_PAGE
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/edf768d88f999dbf.
Report an issue: GitHub.