Mastra 'invocation.errorText?.trim is not a function': fixed in @mastra/memory 1.39.1
A stored failed tool call with an object-valued errorText stopped Observational Memory on every later turn. Upgrade, and mind the @mastra/core peer bump.
Disclosure: I work on MemorySync (I'm Mohammed, the founder). I drafted this page with AI assistance and checked it against the source issue, its linked fix and the docs listed under Sources on Oct 28, 2026. I read the issue, the fix PR and the package changelog; I didn't run Mastra for this page.
The question
mastra-ai/mastra#28407, "Memory TokenCounter crashes on persisted object-valued errorText (still present in 1.39.0)", opened Oct 26, 2026:
TypeError: invocation.errorText?.trim is not a function
TokenCounter.countMessagesAsync() threw it when stored history held a tool invocation whose errorText was an object rather than a string, in both the state: 'result' + isError: true branch and the state: 'output-error' branch. The reporter reproduced it on @mastra/memory 1.38.0, 1.39.0 and 1.39.1-alpha.1, with a JSON round trip and no API keys. The TypeScript type says string; the problem is older or imported rows that don't match it.
Status (checked Nov 9, 2026 via the GitHub API): closed as completed on Oct 26, 2026, by PR #28440, "fix(memory): stop Observational Memory from crashing on object-valued tool errorText". The @mastra/memory changelog lists the fix under 1.39.1 (published Oct 28, 2026). As of Nov 9, 2026 the npm latest is 1.41.0; the fix shipped in 1.39.1 and later. I didn't re-run Mastra on 1.41.0.
Skip the memory plumbing around this fix. Once the fix below is in, the MemorySync Mastra guide shows how to add per-user memory with MemorySync. MemorySync doesn't fix this error itself. When you're ready, see MemorySync. Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings → Billing).
Why it hurts more than one turn
Per the PR, there were two crash sites, both calling errorText?.trim():
TokenCounter.countMessagesAsync()(token-counter.ts)formatMessagesForObserver()(observer-agent.ts), which threwTypeError: inv.errorText?.trim is not a function
Because the bad row stays in the thread, "either error stopped Observational Memory on every later turn," and fixing only one of them didn't help.
Fix
- Upgrade. The changelog's 1.39.1 entry includes a peer-dependency change: if your package manager reports a peer conflict, upgrade @mastra/core first, then memory:
pnpm add @mastra/core@^1.80.0
pnpm add @mastra/memory@latest
If you can't upgrade yet, normalise stored rows before Mastra reads them: for tool invocations where
errorTextisn't a string, storeJSON.stringify(errorText)instead. The reporter's table shows 1.38.0 with that kind of normalisation working, with token counts matching the string control.Look for the source of object errors. Rows written by older code paths or imported from elsewhere are the usual suspects per the issue. Fixing the writer keeps new rows clean.
Common questions
Do I need to delete the thread?
Not after upgrading: the PR adds a shared helper (toolErrorTextToString()) that normalises the value when reading, so existing rows are handled.
Does it affect Mastra without Observational Memory? The crash sites are in Observational Memory's token counter and observer, per the PR.
Skip the plumbing with MemorySync
Disclosure: I build MemorySync.
It doesn't touch Mastra's thread storage, so the upgrade is still the fix. The MemorySync Mastra guide documents processors that recall context before each model call and send the user's message for fact extraction after it; assistant replies aren't stored. For an overview of MemorySync, see the MemorySync homepage.
From the guide:
Unverified (not run):
npm install memorysync-mastra @mastra/core
import { Agent } from "@mastra/core/agent";
import { openai } from "@ai-sdk/openai";
import { createMemorySyncProcessors } from "memorysync-mastra";
const { inputProcessor, outputProcessor } = createMemorySyncProcessors({
userId: "customer-7", // per-end-user scoping
sessionId: "thread-42", // groups the stored facts by thread
});
const agent = new Agent({
id: "assistant",
name: "Assistant",
instructions: "You are a helpful assistant.",
model: openai("your-model"),
inputProcessors: [inputProcessor],
outputProcessors: [outputProcessor],
});
// First conversation
await agent.generate("I'm vegetarian and I fly aisle.");
// Any later call — same user, any thread, any model
const { text } = await agent.generate(
"Book my trip: flight plus a dinner spot.",
);
// The model already saw: vegetarian, aisle seat — injected from memory.
Snippet adapted from the MemorySync Mastra guide; I haven't run it for this page.
Try it: MemorySync is optional here: the steps above work without it. If you want the memory layer from the snippet, see MemorySync, create an API key in the dashboard and drop the snippet into your agent.
Sources
- mastra-ai/mastra#28407 (closed 2026-10-26, completed; read 2026-10-28)
- mastra-ai/mastra#28440 (merged 2026-10-26)
- @mastra/memory CHANGELOG.md on main, 1.39.1 entry; npm
latest1.41.0 as of 2026-11-09 (fix first shipped in 1.39.1; I didn't re-run 1.41.0) - MemorySync docs: docs.memorysync.io/guides/mastra
Next step: read the MemorySync Mastra guide, then see MemorySync. Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings → Billing).
About the author
- Building MemorySync (memorysync.io) — persistent memory infrastructure for AI agents, Cursor, and Claude Code.
Comments
Join the discussionNo comments yet. Be the first to comment.
