can1357/oh-my-pi · error · ToolError
Drag target must be a selector string or { x: number, y: num
Error message
Drag target must be a selector string or { x: number, y: number } point What it means
`#dragPoint` accepts either a selector string or a `{ x, y }` point. If the target is neither (missing/non-finite x or y), it throws this ToolError. It is an argument-shape guard before any geometry lookup.
Source
Thrown at packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts:1037
const object = value as Record<string, unknown>;
return {
x: numberFrom(object.x, 0),
y: numberFrom(object.y, 0),
width: numberFrom(object.width, 0),
height: numberFrom(object.height, 0),
};
}
async #dragPoint(target: DragTarget): Promise<{ x: number; y: number }> {
if (typeof target === "string") {
const box = await this.#selectorBox(this.#selectorSpec(target));
if (!box) throw new ToolError(`Drag selector did not resolve to a visible element: ${target}`);
return { x: box.x + box.width / 2, y: box.y + box.height / 2 };
}
if (Number.isFinite(target.x) && Number.isFinite(target.y)) {
return { x: target.x, y: target.y };
}
throw new ToolError("Drag target must be a selector string or { x: number, y: number } point");
}
async #installResponseObserver(): Promise<void> {
await this.#evalScript<boolean>(RESPONSE_OBSERVER_SCRIPT);
}
async #responseCursor(): Promise<number> {
const value = await this.#evalScript<unknown>(
"(() => Math.max(0, ((globalThis.__ompCmuxResponses && globalThis.__ompCmuxResponses.nextId) || 1) - 1))()",
);
return numberFrom(value, 0);
}
async #responseRecordsAfter(id: number): Promise<CmuxResponseRecord[]> {
const value = await this.#evalScript<unknown>(
`(() => ((globalThis.__ompCmuxResponses && globalThis.__ompCmuxResponses.records) || []).filter(record => record.id > ${JSON.stringify(id)}))()`,
);
if (!Array.isArray(value)) return [];View on GitHub (pinned to 9690622007)
Solutions
- Ensure the point target has finite numeric x and y: coerce with Number() and validate Number.isFinite before calling.
- Pass a selector string instead of a point if you have an element, not coordinates.
- Fix the call site so the right overload is used — don't pass an empty or partial object.
Example fix
// before
tab.drag('.item', { x: opts.left, y: opts.top }); // opts.left may be undefined/'100'
// after
const x = Number(opts.left), y = Number(opts.top);
if (Number.isFinite(x) && Number.isFinite(y)) tab.drag('.item', { x, y });
else tab.drag('.item', '.dropzone'); Defensive patterns
Strategy: validation
Validate before calling
function assertDragPoint(t: unknown): asserts t is string | { x: number; y: number } {
if (typeof t === 'string') return;
const p = t as { x?: unknown; y?: unknown };
if (!p || !Number.isFinite(Number(p.x)) || !Number.isFinite(Number(p.y)))
throw new TypeError(`drag target must be a selector or {x,y} point, got ${JSON.stringify(t)}`);
} Type guard
function isDragPoint(t: unknown): t is { x: number; y: number } {
return typeof t === 'object' && t !== null && Number.isFinite((t as { x: unknown }).x as number) && Number.isFinite((t as { y: unknown }).y as number);
} Prevention
- Type drag-target parameters as `string | { x: number; y: number }` so bad shapes fail at compile time.
- Coerce and Number.isFinite-check coordinates coming from configs/JSON.
- Never pass undefined/empty objects as targets.
When it happens
Trigger: Passing an object with missing, null, NaN, or string-typed x/y (e.g. {x: '100', y: 200}); passing undefined/null; passing some other object shape the API doesn't recognize.
Common situations: Coordinates coming from an external config or JSON where numbers were parsed as strings; a variable accidentally undefined; mixing up the drag API signature (selectors vs points).
Related errors
- Invalid pattern: {err}
- unknown function: {0}
- unknown key binding function: {0}
- Destination option ${key} must be a string
- Destination option ${key} must be a finite number
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/fa4b110769c9a4ad.
Report an issue: GitHub.