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

  1. Ensure the point target has finite numeric x and y: coerce with Number() and validate Number.isFinite before calling.
  2. Pass a selector string instead of a point if you have an element, not coordinates.
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/fa4b110769c9a4ad. Report an issue: GitHub.