{"record":{"id":"1f0dc141f05c48b9","repo":"can1357/oh-my-pi","slug":"read-only-run-method-requires-read-only-fal","errorCode":null,"errorMessage":"read-only run: '${method}' requires read_only: false","messagePattern":"read-only run: '(.+?)' requires read_only: false","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/computer/worker.ts","lineNumber":178,"sourceCode":"\t\t? chord\n\t\t\t\t.split(\"+\")\n\t\t\t\t.map(key => key.trim())\n\t\t\t\t.filter(Boolean)\n\t\t: chord;\n}\n\nfunction matchesFilter(window: DesktopWindow, filter?: WindowFilter): boolean {\n\tif (!filter) return true;\n\tconst app = filter.app?.toLocaleLowerCase();\n\tconst title = filter.title?.toLocaleLowerCase();\n\treturn (\n\t\t(!app || window.app.toLocaleLowerCase().includes(app)) &&\n\t\t(!title || window.title.toLocaleLowerCase().includes(title))\n\t);\n}\n\nfunction guardRun(context: ComputerRunContext, method: string): void {\n\tif (context.readOnly) throw new ToolError(`read-only run: '${method}' requires read_only: false`);\n\tthrowIfAborted(context.signal);\n}\n\nasync function captureScreenshot(\n\tsession: NativeDesktopSession,\n\tgetContext: RunContextAccessor,\n\ttarget: string,\n\toptions?: ScreenshotOptions,\n): Promise<{ path: string; width: number; height: number }> {\n\tconst context = getContext();\n\tconst frame = await nativeCall(context.signal, () =>\n\t\tsession.capture(target, {\n\t\t\tmaxWidth: context.snapshot.captureMaxWidth,\n\t\t\tmaxHeight: context.snapshot.captureMaxHeight,\n\t\t}),\n\t);\n\tconst destination = path.join(os.tmpdir(), `omp-computer-${Snowflake.next()}.png`);\n\tawait Bun.write(destination, frame.data);","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/computer/worker.ts#L160-L196","documentation":"The computer worker runs in a read-only mode where mutating desktop operations are forbidden. guardRun() is invoked by every mutating facade method (setValue, perform, press, click, focus, doubleClick); if the run context has readOnly=true, the call throws this ToolError before touching the desktop.","triggerScenarios":"Executing computer-run code that calls click()/press()/setValue()/perform()/focus()/doubleClick() while the tool was opened with read_only: true.","commonSituations":"An agent or script tries to interact with the UI during a screenshot/inspection-only session; the session config defaulted to read-only; policy restricts the tool to observation but generated code attempts mutation.","solutions":["Re-open (or reconfigure) the computer session with read_only: false if mutation is intended.","Remove or branch around mutating calls when operating in read-only mode; use screenshots/queries only.","Check context.readOnly in generated code before calling mutating methods."],"exampleFix":"// inside computer run code\n// before\nawait desktop.win({ app: \"Notes\" }).click();\n\n// after\nif (!context.readOnly) {\n  await desktop.win({ app: \"Notes\" }).click();\n} else {\n  await desktop.screenshot(); // read-only alternative\n}","handlingStrategy":"validation","validationCode":"// inside computer run code\nif (context.readOnly) {\n  throw new Error(\"cannot click/press/setValue: session is read-only\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  await run(code);\n} catch (err) {\n  if (err instanceof ToolError && /read-only run/.test(err.message)) {\n    // fall back to observation-only operations (screenshot, query)\n  } else throw err;\n}","preventionTips":["Generate read-only-safe code when the session is opened with read_only: true.","Check context.readOnly before any mutating facade call in scripts.","Document the read_only flag in prompts so the model doesn't attempt mutations."],"tags":["read-only","permissions","computer-tool","guard"],"backgroundTag":"read-only-mode-violation","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}