can1357/oh-my-pi · error · Error
Tool "write" not available: this session cannot download res
Error message
Tool "write" not available: this session cannot download resources to disk.
What it means
Reading an MCP resource with a downloadPath requires the session to allow direct file mutation (equivalent to the 'write' tool being enabled). If the session was started without file-write permissions, the download is rejected up front rather than failing later at the write step. This is a capability gate, not a bug.
Source
Thrown at packages/coding-agent/src/cursor.ts:793
* workspace-relative path and answer with the path alone, so a large binary
* lands on disk instead of in the model's context. That makes it a workspace
* mutation reached without a registry tool, so it is gated exactly like the
* native `delete` frame — on the session actually granting a file-writing
* tool, and on the user's `write`-tier policy. The gate runs before the read
* so a refused download never fetches the resource either.
*/
async readMcpResource({
server,
uri,
downloadPath,
}: {
server: string;
uri: string;
downloadPath?: string;
}): Promise<CursorMcpResourceContent | null> {
if (downloadPath) {
if (!allowsDirectFileMutation(this.options)) {
throw new Error('Tool "write" not available: this session cannot download resources to disk.');
}
const refusal = refuseByWritePolicy(this.options, "write", downloadPath);
if (refusal) throw new Error(refusal);
}
const mcp = this.options.mcpResources;
if (!mcp) return null;
const read = await mcp.readServerResource(server, uri);
if (!read) return null;
// The mime type must describe the bytes actually sent, not whatever item
// happened to be first: an image blob followed by a text note would
// otherwise label the text `image/png` and mislead the model about what
// it is holding. Each branch below takes the type from its own producer.
const textItems = read.contents.filter(item => item.text !== undefined);
const texts = textItems.map(item => item.text as string);
const blobItem = read.contents.find(item => item.blob !== undefined);
const blob = blobItem?.blob;
const textMimeType = textItems[0]?.mimeType;
const blobMimeType = blobItem?.mimeType;View on GitHub (pinned to 9690622007)
Solutions
- Enable the write tool / direct file mutation in the session options, or restart the session in a mode that grants it.
- Omit downloadPath and consume the resource bytes in-memory instead of writing to disk.
- If running via the CLI, remove the permission restriction or approve write access for the session/workspace.
Example fix
// before: read-only session options
const handlers = new CursorExecHandlers({ ...options, toolPermissions: { write: false } });
// after: allow writes (or don't pass downloadPath)
const handlers = new CursorExecHandlers({ ...options, toolPermissions: { write: true } }); Defensive patterns
Strategy: try-catch
Validate before calling
if (downloadPath && !sessionAllowsDirectFileMutation) {
// don't request a download at all; read in-memory instead
delete request.downloadPath;
} Try / catch
try {
return await readMcpResource({ server, uri, downloadPath });
} catch (e) {
if (String(e.message).includes('Tool "write" not available')) {
return readMcpResource({ server, uri }); // fall back to in-memory content
} else throw e;
} Prevention
- Only pass downloadPath when the session's write tool is enabled.
- Check session tool permissions before offering download affordances.
- In SDK/embedding contexts, grant write access or omit downloadPath.
When it happens
Trigger: Calling readMcpResource (Cursor exec bridge) with downloadPath set in a session whose options disable direct file mutation — e.g. write tool disabled, sandboxed/read-only session mode, or an SDK embedding that did not grant file tools.
Common situations: Running the agent in a restricted mode (plan/read-only) and asking it to save a fetched MCP resource to disk; embedding the library with a limited tool allowlist; CI sessions without write access.
Related errors
- (dynamic write-policy refusal message: throw new Error(refus
- ENXIO
- Refusing to download onto a non-regular file: ${absolutePath
- Refusing to download onto a file with ${stat.nlink} hard lin
- Refusing to download outside the workspace: ${downloadPath}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/acdeaf5f6fe49446.
Report an issue: GitHub.