can1357/oh-my-pi · error · ToolError
Drag selector did not resolve
Error message
Drag ${role} selector did not resolve: ${target} What it means
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.
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.
Example fix
// before
await tab.drag({ from: "#item-3", to: "#trash" }); // #item-3 removed after re-render
// after
await tab.observe();
const box = await tab.evaluate(() => document.querySelector("#trash")?.getBoundingClientRect());
await tab.drag({ from: "#item-3", to: { x: box.x + box.width / 2, y: box.y + box.height / 2 } }); Defensive patterns
Strategy: validation
Validate before calling
for (const endpoint of [from, to]) {
if (typeof endpoint === "string") {
const ok = await tab.evaluate((sel) => !!document.querySelector(sel), endpoint);
if (!ok) throw new Error(`drag endpoint selector missing: ${endpoint}`);
}
} Type guard
function isDragTarget(t: unknown): t is string | { x: number; y: number } {
return typeof t === "string"
|| (!!t && typeof t === "object" && typeof (t as any).x === "number" && typeof (t as any).y === "number");
} Try / catch
try {
await tab.drag({ from, to });
} catch (err) {
if (err.message.includes("Drag ") && err.message.includes("did not resolve")) {
await tab.observe();
return tab.drag({ from: resolveFresh(from), to: resolveFresh(to) });
}
throw err;
} Prevention
- 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
When it happens
Trigger: tab.drag({ from: "#src", to: "#dst" }) where either selector matches nothing, or an expired aria-ref id is supplied for either endpoint.
Common situations: 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.
Related errors
- No element matched
- Screenshot selector did not resolve to an element
- Browser selector must be a string; got
- Drag element has no bounding box (likely not visible)
- Drag must be a selector string or point. Got
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b36bde05a3f079d1.
Report an issue: GitHub.
Appendix: source
Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:1979
});
output.push({ type: "text", text: lines.join("\n") });
output.push({ type: "image", data: resized.data, mimeType: resized.mimeType });
}
return dest;
}
async #drag(from: DragTarget, to: DragTarget, signal: AbortSignal): Promise<void> {
const page = this.#requirePage();
const resolveDragPoint = async (
target: DragTarget,
role: "from" | "to",
): Promise<{ x: number; y: number; handle?: ElementHandle }> => {
if (typeof target === "string") {
const handle =
parseAriaRefSelector(target) !== null
? await this.#resolveAriaRef(target)
: asElementHandle(await untilAborted(signal, () => page.$(normalizeSelector(target))));
if (!handle) throw new ToolError(`Drag ${role} selector did not resolve: ${target}`);
const box = (await untilAborted(signal, () => handle.boundingBox())) as {
x: number;
y: number;
width: number;
height: number;
} | null;
if (!box) {
await handle.dispose().catch(() => undefined);
throw new ToolError(`Drag ${role} element has no bounding box (likely not visible): ${target}`);
}
return { x: box.x + box.width / 2, y: box.y + box.height / 2, handle };
}
if (
target !== null &&
typeof target === "object" &&
typeof (target as { x: unknown }).x === "number" &&
typeof (target as { y: unknown }).y === "number"
) {View on GitHub (pinned to 9690622007)