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

  1. Stringify objects before writing: `write(path, JSON.stringify(obj))`.
  2. For binary data, convert to ArrayBuffer or Uint8Array first (e.g. `await res.arrayBuffer()` or `new TextEncoder().encode(str)`).
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/3ac51038d823775a. Report an issue: GitHub.