{"record":{"id":"3ac51038d823775a","repo":"can1357/oh-my-pi","slug":"write-expects-string-blob-arraybuffer-or-type","errorCode":null,"errorMessage":"write() expects string, Blob, ArrayBuffer, or TypedArray data","messagePattern":"write\\(\\) expects string, Blob, ArrayBuffer, or TypedArray data","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/js/shared/helpers.ts","lineNumber":59,"sourceCode":"export function createHelpers(ctx: HelperContext): HelperBundle {\n\treturn {\n\t\tread: async (rawPath, options = {}) => {\n\t\t\tconst { filePath, file, size } = await resolveRegularFile(ctx, rawPath);\n\t\t\tlet text = await file.text();\n\t\t\tconst offset = typeof options.offset === \"number\" ? options.offset : 1;\n\t\t\tconst limit = typeof options.limit === \"number\" ? options.limit : undefined;\n\t\t\tif (offset > 1 || limit !== undefined) {\n\t\t\t\tconst lines = text.split(/\\r?\\n/);\n\t\t\t\tconst start = Math.max(0, offset - 1);\n\t\t\t\tconst end = limit !== undefined ? start + limit : lines.length;\n\t\t\t\ttext = lines.slice(start, end).join(\"\\n\");\n\t\t\t}\n\t\t\tctx.emitStatus({ op: \"read\", path: filePath, bytes: size, chars: text.length });\n\t\t\treturn text;\n\t\t},\n\t\twriteFile: async (rawPath, data) => {\n\t\t\tif (!isWriteData(data)) {\n\t\t\t\tthrow new ToolError(\"write() expects string, Blob, ArrayBuffer, or TypedArray data\");\n\t\t\t}\n\t\t\tconst filePath = resolveHelperPath(ctx, rawPath, \"write\");\n\t\t\tif (typeof data === \"string\" || data instanceof Blob || data instanceof ArrayBuffer) {\n\t\t\t\tawait Bun.write(filePath, data);\n\t\t\t} else {\n\t\t\t\tawait Bun.write(filePath, new Uint8Array(data.buffer, data.byteOffset, data.byteLength));\n\t\t\t}\n\t\t\tctx.emitStatus({ op: \"write\", path: filePath, bytes: getDataSize(data) });\n\t\t\treturn filePath;\n\t\t},\n\t\tenv: (key, value) => {\n\t\t\tif (!key) {\n\t\t\t\tconst merged = Object.fromEntries(Object.entries(getMergedEnv(ctx)).sort(([a], [b]) => a.localeCompare(b)));\n\t\t\t\tctx.emitStatus({ op: \"env\", count: Object.keys(merged).length, keys: Object.keys(merged).slice(0, 20) });\n\t\t\t\treturn merged;\n\t\t\t}\n\t\t\tif (value !== undefined) {\n\t\t\t\tctx.env.set(key, value);","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/js/shared/helpers.ts#L41-L77","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait write(\"out.json\", { result: 42 });\n// after\nawait write(\"out.json\", JSON.stringify({ result: 42 }));","handlingStrategy":"type-guard","validationCode":"function assertWriteData(data: unknown): asserts data is string | Blob | ArrayBuffer | ArrayBufferView {\n\tif (!(typeof data === \"string\" || data instanceof Blob || data instanceof ArrayBuffer || ArrayBuffer.isView(data))) {\n\t\tthrow new TypeError(\"write() expects string, Blob, ArrayBuffer, or TypedArray\");\n\t}\n}","typeGuard":"function isWriteData(data: unknown): data is string | Blob | ArrayBuffer | ArrayBufferView {\n\treturn typeof data === \"string\" || data instanceof Blob || data instanceof ArrayBuffer || ArrayBuffer.isView(data);\n}","tryCatchPattern":"try {\n\tawait write(path, data);\n} catch (err) {\n\tif (String(err?.message).startsWith(\"write() expects\")) {\n\t\tdata = JSON.stringify(data);\n\t\tawait write(path, data);\n\t} else throw err;\n}","preventionTips":["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."],"tags":["validation","file-write","sandbox","types"],"backgroundTag":"invalid-write-data-type","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}