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

  1. Ensure the system message is a plain CoreMessage: { role: 'system', content: 'your instructions' } with content as a string or a valid content-part array.
  2. Check the receivedMessage in error details to see what shape actually arrived and fix the producer that serialized it.
  3. If migrating from an older Mastra/AI SDK version, convert stored messages to the current CoreMessage/DBMessage format before adding them.
  4. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/edf768d88f999dbf. Report an issue: GitHub.