can1357/oh-my-pi · error · ToolError
write() expects string, Blob, ArrayBuffer, or TypedArray dat
Error message
write() expects string, Blob, ArrayBuffer, or TypedArray data
What it means
The JS eval sandbox helper `write()` (writeFile) only accepts string, Blob, ArrayBuffer, or TypedArray data; `isWriteData` gates the Bun.write call. Passing anything else (plain object, number, null, DataView-hostile values) throws this ToolError instead of writing garbage bytes to disk.
Source
Thrown at packages/coding-agent/src/eval/js/shared/helpers.ts:59
export function createHelpers(ctx: HelperContext): HelperBundle {
return {
read: async (rawPath, options = {}) => {
const { filePath, file, size } = await resolveRegularFile(ctx, rawPath);
let text = await file.text();
const offset = typeof options.offset === "number" ? options.offset : 1;
const limit = typeof options.limit === "number" ? options.limit : undefined;
if (offset > 1 || limit !== undefined) {
const lines = text.split(/\r?\n/);
const start = Math.max(0, offset - 1);
const end = limit !== undefined ? start + limit : lines.length;
text = lines.slice(start, end).join("\n");
}
ctx.emitStatus({ op: "read", path: filePath, bytes: size, chars: text.length });
return text;
},
writeFile: async (rawPath, data) => {
if (!isWriteData(data)) {
throw new ToolError("write() expects string, Blob, ArrayBuffer, or TypedArray data");
}
const filePath = resolveHelperPath(ctx, rawPath, "write");
if (typeof data === "string" || data instanceof Blob || data instanceof ArrayBuffer) {
await Bun.write(filePath, data);
} else {
await Bun.write(filePath, new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
}
ctx.emitStatus({ op: "write", path: filePath, bytes: getDataSize(data) });
return filePath;
},
env: (key, value) => {
if (!key) {
const merged = Object.fromEntries(Object.entries(getMergedEnv(ctx)).sort(([a], [b]) => a.localeCompare(b)));
ctx.emitStatus({ op: "env", count: Object.keys(merged).length, keys: Object.keys(merged).slice(0, 20) });
return merged;
}
if (value !== undefined) {
ctx.env.set(key, value);View on GitHub (pinned to 9690622007)
Solutions
- Stringify objects before writing: `write(path, JSON.stringify(obj))`.
- For binary data, convert to ArrayBuffer or Uint8Array first (e.g. `await res.arrayBuffer()` or `new TextEncoder().encode(str)`).
- Check the value's runtime type before calling write; only string, Blob, ArrayBuffer, and TypedArray (ArrayBufferView backed by ArrayBuffer) pass the guard.
Example fix
// before
await write("out.json", { result: 42 });
// after
await write("out.json", JSON.stringify({ result: 42 })); Defensive patterns
Strategy: type-guard
Validate before calling
function assertWriteData(data: unknown): asserts data is string | Blob | ArrayBuffer | ArrayBufferView {
if (!(typeof data === "string" || data instanceof Blob || data instanceof ArrayBuffer || ArrayBuffer.isView(data))) {
throw new TypeError("write() expects string, Blob, ArrayBuffer, or TypedArray");
}
} Type guard
function isWriteData(data: unknown): data is string | Blob | ArrayBuffer | ArrayBufferView {
return typeof data === "string" || data instanceof Blob || data instanceof ArrayBuffer || ArrayBuffer.isView(data);
} Try / catch
try {
await write(path, data);
} catch (err) {
if (String(err?.message).startsWith("write() expects")) {
data = JSON.stringify(data);
await write(path, data);
} else throw err;
} Prevention
- Always JSON.stringify objects/arrays before write().
- Convert fetch bodies with .text()/.arrayBuffer()/.blob() before writing.
- Use TextEncoder().encode() for strings you want as bytes.
When it happens
Trigger: Calling `write(path, data)` from JS eval code with `data` that is not string/Blob/ArrayBuffer/TypedArray — e.g. writing a JSON object without JSON.stringify, passing null/undefined, passing a DataView or a plain array.
Common situations: Agent-generated eval code doing `write('out.json', {a:1})` (object not stringified); returning a fetch response object instead of its body; passing a number or boolean; forgetting `await fetch(...).arrayBuffer()` before writing binary data.
Related errors
- Protocol paths are not supported by ${op}(): ${rawPath}
- Invalid URL encoding in ${scheme}:// path: ${rawPath}
- Absolute paths are not allowed in ${scheme}:// URLs: ${rawPa
- Path traversal (..) is not allowed in ${scheme}:// URLs: ${r
- Directory paths are not supported by read(): ${filePath}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/3ac51038d823775a.
Report an issue: GitHub.