{"record":{"id":"b87b896d1d2b1455","repo":"can1357/oh-my-pi","slug":"could-not-resolve-image-attachment-readpath","errorCode":null,"errorMessage":"Could not resolve image attachment '${readPath}'. Available attachment URIs: ${availableUris}. Use one of the listed attachment URIs, or attach an image first when none are available.","messagePattern":"Could not resolve image attachment '(.+?)'\\. Available attachment URIs: (.+?)\\. Use one of the listed attachment URIs, or attach an image first when none are available\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/read.ts","lineNumber":1097,"sourceCode":"\n\tasync #executeInner(\n\t\t_toolCallId: string,\n\t\tparams: ReadParams,\n\t\tsignal?: AbortSignal,\n\t\t_onUpdate?: AgentToolUpdateCallback<ReadToolDetails>,\n\t\t_toolContext?: AgentToolContext,\n\t): Promise<AgentToolResult<ReadToolDetails>> {\n\t\tlet { path: readPath } = params;\n\t\tif (readPath.startsWith(\"file://\")) {\n\t\t\treadPath = expandPath(readPath);\n\t\t}\n\n\t\tif (IMAGE_ATTACHMENT_URI_REGEX.test(readPath)) {\n\t\t\tconst attachments = this.session.getImageAttachments?.() ?? [];\n\t\t\tconst attachment = attachments.find(entry => entry.uri === readPath);\n\t\t\tif (!attachment) {\n\t\t\t\tconst availableUris = attachments.map(entry => entry.uri).join(\", \") || \"none\";\n\t\t\t\tthrow new ToolError(\n\t\t\t\t\t`Could not resolve image attachment '${readPath}'. Available attachment URIs: ${availableUris}. Use one of the listed attachment URIs, or attach an image first when none are available.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treadPath = attachment.sourcePath;\n\t\t}\n\n\t\tconst conflictUri = parseConflictUri(readPath);\n\t\tif (conflictUri) {\n\t\t\tif (conflictUri.id === \"*\") {\n\t\t\t\tthrow new ToolError(\n\t\t\t\t\t\"Reading `conflict://*` is not supported — wildcards are write-only. Use the `<path>:conflicts` read selector for the full list of conflicts in a file, or read `conflict://<N>` to inspect a single block.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn this.#readConflictRegion(conflictUri.id, conflictUri.scope);\n\t\t}\n\t\tconst displayMode = resolveFileDisplayMode(this.session);\n\n\t\tconst parsedUrlTarget = parseReadUrlTarget(readPath);","sourceCodeStart":1079,"sourceCodeEnd":1115,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/read.ts#L1079-L1115","documentation":"Reads of image attachment URIs (matching IMAGE_ATTACHMENT_URI_REGEX) are resolved against the session's registered image attachments. If no attachment's uri equals the requested readPath, the tool throws a ToolError listing every available attachment URI (or 'none') and instructs the caller to use one of them or attach an image first.","triggerScenarios":"Reading 'attachment://<id>' (or similar) with a stale/typo'd URI; the attachment was dropped from session history; a new session with no attachments while the model replays an old URI.","commonSituations":"Model hallucinating or misremembering an attachment URI; referencing an image attached in a previous session/turn that was garbage-collected; whitespace or case mismatch in the URI.","solutions":["Copy the URI exactly from the 'Available attachment URIs' list in the message.","If the list is 'none', attach the image to the session first (drag-and-drop/paste into the client) before reading.","Re-attach the image if the session was restarted and use the new URI.","Fall back to reading the original file from disk by path if it exists locally."],"exampleFix":"// before\nread('attachment://img-42') // stale id\n// after\nread('attachment://img-7f3a') // URI copied from the available list","handlingStrategy":"validation","validationCode":"const attachments = session.getImageAttachments?.() ?? [];\nconst uris = new Set(attachments.map(a => a.uri));\nif (!uris.has(attachmentUri)) {\n  // fall back to the original file on disk, or attach first\n  throw new Error(`Unknown attachment ${attachmentUri}; known: ${[...uris].join(', ') || 'none'}`);\n}","typeGuard":"function isKnownAttachment(uri: string, session: Session): boolean {\n  return (session.getImageAttachments?.() ?? []).some(a => a.uri === uri);\n}","tryCatchPattern":"try {\n  return await readTool.execute({ path: uri });\n} catch (e) {\n  if (e instanceof ToolError && e.message.startsWith('Could not resolve image attachment')) {\n    const available = e.message.match(/Available attachment URIs: ([^.]*)/)?.[1] ?? 'none';\n    const first = available.split(', ')[0];\n    return first && first !== 'none' ? readTool.execute({ path: first }) : null;\n  }\n  throw e;\n}","preventionTips":["Copy attachment URIs verbatim from the tool/client output — never reconstruct them.","Re-resolve URIs after session restarts; attachments don't persist across sessions.","Check the available-URIs list embedded in the error before retrying.","If the source file exists on disk, read it by path instead of via attachment URI."],"tags":["attachment","uri","resolution"],"backgroundTag":"attachment-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}