{"record":{"id":"b36bde05a3f079d1","repo":"can1357/oh-my-pi","slug":"drag-role-selector-did-not-resolve-target","errorCode":null,"errorMessage":"Drag ${role} selector did not resolve: ${target}","messagePattern":"Drag (.+?) selector did not resolve: (.+?)","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/tab-worker.ts","lineNumber":1979,"sourceCode":"\t\t\t});\n\t\t\toutput.push({ type: \"text\", text: lines.join(\"\\n\") });\n\t\t\toutput.push({ type: \"image\", data: resized.data, mimeType: resized.mimeType });\n\t\t}\n\t\treturn dest;\n\t}\n\n\tasync #drag(from: DragTarget, to: DragTarget, signal: AbortSignal): Promise<void> {\n\t\tconst page = this.#requirePage();\n\t\tconst resolveDragPoint = async (\n\t\t\ttarget: DragTarget,\n\t\t\trole: \"from\" | \"to\",\n\t\t): Promise<{ x: number; y: number; handle?: ElementHandle }> => {\n\t\t\tif (typeof target === \"string\") {\n\t\t\t\tconst handle =\n\t\t\t\t\tparseAriaRefSelector(target) !== null\n\t\t\t\t\t\t? await this.#resolveAriaRef(target)\n\t\t\t\t\t\t: asElementHandle(await untilAborted(signal, () => page.$(normalizeSelector(target))));\n\t\t\t\tif (!handle) throw new ToolError(`Drag ${role} selector did not resolve: ${target}`);\n\t\t\t\tconst box = (await untilAborted(signal, () => handle.boundingBox())) as {\n\t\t\t\t\tx: number;\n\t\t\t\t\ty: number;\n\t\t\t\t\twidth: number;\n\t\t\t\t\theight: number;\n\t\t\t\t} | null;\n\t\t\t\tif (!box) {\n\t\t\t\t\tawait handle.dispose().catch(() => undefined);\n\t\t\t\t\tthrow new ToolError(`Drag ${role} element has no bounding box (likely not visible): ${target}`);\n\t\t\t\t}\n\t\t\t\treturn { x: box.x + box.width / 2, y: box.y + box.height / 2, handle };\n\t\t\t}\n\t\t\tif (\n\t\t\t\ttarget !== null &&\n\t\t\t\ttypeof target === \"object\" &&\n\t\t\t\ttypeof (target as { x: unknown }).x === \"number\" &&\n\t\t\t\ttypeof (target as { y: unknown }).y === \"number\"\n\t\t\t) {","sourceCodeStart":1961,"sourceCodeEnd":1997,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-worker.ts#L1961-L1997","documentation":"The drag helper resolves each endpoint (from/to) to a point: string targets are resolved through ARIA-ref or CSS page.$. If the selector matches no element, this ToolError names which endpoint (from or to) failed and echoes the target. The drag is aborted before any mouse events are dispatched.","triggerScenarios":"tab.drag({ from: \"#src\", to: \"#dst\" }) where either selector matches nothing, or an expired aria-ref id is supplied for either endpoint.","commonSituations":"Drag source/target removed by a re-render between observe and drag; selector typo or wrong document/frame; HTML5 drag targets that exist visually but are re-created dynamically on hover; stale aria ref after page navigation.","solutions":["Re-run tab.observe() to refresh aria refs, then retry the drag with current refs.","Validate both selectors resolve (document.querySelector) on the live page before dragging.","If the target is inside an iframe or shadow DOM, scope the selector correctly or use explicit { x, y } coordinates.","Use coordinate points { x, y } from a fresh screenshot when selectors are unstable."],"exampleFix":"// before\nawait tab.drag({ from: \"#item-3\", to: \"#trash\" }); // #item-3 removed after re-render\n// after\nawait tab.observe();\nconst box = await tab.evaluate(() => document.querySelector(\"#trash\")?.getBoundingClientRect());\nawait tab.drag({ from: \"#item-3\", to: { x: box.x + box.width / 2, y: box.y + box.height / 2 } });","handlingStrategy":"validation","validationCode":"for (const endpoint of [from, to]) {\n  if (typeof endpoint === \"string\") {\n    const ok = await tab.evaluate((sel) => !!document.querySelector(sel), endpoint);\n    if (!ok) throw new Error(`drag endpoint selector missing: ${endpoint}`);\n  }\n}","typeGuard":"function isDragTarget(t: unknown): t is string | { x: number; y: number } {\n  return typeof t === \"string\"\n    || (!!t && typeof t === \"object\" && typeof (t as any).x === \"number\" && typeof (t as any).y === \"number\");\n}","tryCatchPattern":"try {\n  await tab.drag({ from, to });\n} catch (err) {\n  if (err.message.includes(\"Drag \") && err.message.includes(\"did not resolve\")) {\n    await tab.observe();\n    return tab.drag({ from: resolveFresh(from), to: resolveFresh(to) });\n  }\n  throw err;\n}","preventionTips":["Re-observe before dragging to refresh refs","Prefer explicit { x, y } coordinates for flaky elements","Verify both selectors exist before the call","Handle SPA re-renders by re-resolving endpoints right before dragging"],"tags":["browser","drag","selector","element-not-found"],"backgroundTag":"selector-did-not-resolve","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}