can1357/oh-my-pi · error · ToolError
Drag ${role} selector did not resolve: ${target}
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.
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)
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 ${spec.raw}
- Screenshot selector did not resolve to an element
- Browser selector must be a string; got ${kind}. tab.click/ty
- tab.ariaSnapshot: selector ${__sel} matched no element
- No page target matched ${JSON.stringify(options.matcher)}. A
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b36bde05a3f079d1.
Report an issue: GitHub.