mastra-ai/mastra · error · Error

countCoreSystemMessageTokens can only be used with system me

Error message

countCoreSystemMessageTokens can only be used with system messages, received role: ${message.role}

What it means

countCoreSystemMessageTokens is a private helper that tokenizes a message retrieved from messageList.getAllSystemMessages(). It guards that the passed message actually has role 'system'; anything else indicates an internal invariant violation (wrong list source or malformed MessageList contents) and throws a plain Error.

Source

Thrown at packages/core/src/processors/processors/token-limiter.ts:229

        },
      );
    }

    // Remove messages that don't fit within the token budget
    const keepIds = new Set(messagesToKeep.map(m => m.id));
    const idsToRemove = messages.filter(m => !keepIds.has(m.id)).map(m => m.id);
    if (idsToRemove.length > 0) {
      messageList.removeByIds(idsToRemove);
    }
  }

  /**
   * Count tokens for a system message. Accepts both untagged and tagged system messages
   * read from `messageList.getAllSystemMessages()`. Only string content is supported.
   */
  private async countCoreSystemMessageTokens(message: CoreMessageV4): Promise<number> {
    if (message.role !== 'system') {
      throw new Error(
        `countCoreSystemMessageTokens can only be used with system messages, received role: ${message.role}`,
      );
    }

    if (typeof message.content !== 'string') {
      throw new Error('countCoreSystemMessageTokens: System message content must be a string');
    }

    const tokenString = message.role + message.content;

    return this.countTokens(tokenString) + TokenLimiterProcessor.TOKENS_PER_MESSAGE;
  }

  /**
   * Count tokens for an input message, including overhead for message structure
   */
  private async countInputMessageTokens(message: MastraDBMessage): Promise<number> {
    let tokenString = message.role;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Check what you are putting into the MessageList as system messages — only role:'system' entries belong there.
  2. If using a custom processor, only call countCoreSystemMessageTokens with messages from getAllSystemMessages().
  3. Update @mastra/core to the latest patch — this may be a fixed MessageList regression.
  4. Log the offending message.role and message content to find the code path inserting the wrong role.

Example fix

// before
countCoreSystemMessageTokens(someUserMessage);
// after
if (msg.role === 'system') countCoreSystemMessageTokens(msg);
Defensive patterns

Strategy: type-guard

Type guard

function isSystemMessage(m) {
  return !!m && m.role === 'system';
}
// use: if (isSystemMessage(msg)) countCoreSystemMessageTokens(msg);

Prevention

When it happens

Trigger: processInputStep passes a CoreMessageV4 whose role is not 'system' to countCoreSystemMessageTokens — e.g. a MessageList regression where getAllSystemMessages() returns non-system messages, or custom code inserting developer/user-role messages into the system bucket.

Common situations: Custom processors or memory code injecting messages with unexpected roles tagged as system; upgrading @mastra/core and hitting an internal invariant change; subclassing/patching the processor and calling the helper directly with arbitrary messages.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


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