{"record":{"id":"9d0064e83186d320","repo":"can1357/oh-my-pi","slug":"drag-role-must-be-a-selector-string-or-x-num","errorCode":null,"errorMessage":"Drag ${role} must be a selector string or { x: number, y: number } point. Got: ${typeof target}","messagePattern":"Drag (.+?) must be a selector string or (.+?) point\\. Got: (.+?)","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/tab-worker.ts","lineNumber":2000,"sourceCode":"\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) {\n\t\t\t\treturn { x: (target as { x: number }).x, y: (target as { y: number }).y };\n\t\t\t}\n\t\t\tthrow new ToolError(\n\t\t\t\t`Drag ${role} must be a selector string or { x: number, y: number } point. Got: ${typeof target}`,\n\t\t\t);\n\t\t};\n\t\tconst start = await resolveDragPoint(from, \"from\");\n\t\tlet end: { x: number; y: number; handle?: ElementHandle } | undefined;\n\t\ttry {\n\t\t\tend = await resolveDragPoint(to, \"to\");\n\t\t\tawait untilAborted(signal, () => page.mouse.move(start.x, start.y));\n\t\t\tawait untilAborted(signal, () => page.mouse.down());\n\t\t\tawait untilAborted(signal, () => page.mouse.move(end!.x, end!.y, { steps: 12 }));\n\t\t\tawait untilAborted(signal, () => page.mouse.up());\n\t\t} finally {\n\t\t\tif (start.handle) await start.handle.dispose().catch(() => undefined);\n\t\t\tif (end?.handle) await end.handle.dispose().catch(() => undefined);\n\t\t}\n\t}\n\n\tasync #select(selector: string, values: string[], timeoutMs: number, signal: AbortSignal): Promise<string[]> {","sourceCodeStart":1982,"sourceCodeEnd":2018,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-worker.ts#L1982-L2018","documentation":"The drag point resolver accepts only a selector string or a { x, y } numeric point object. Anything else (number, null, array, object missing x/y) fails this ToolError, which reports the offending typeof. This is an argument-shape validation error thrown before any mouse interaction.","triggerScenarios":"tab.drag({ from: 123, to: ... }), from: null, { from: { x: 10 } } (missing y), or passing DOM element handles/position objects from another API instead of plain { x, y }.","commonSituations":"Passing a bounding-box object (x,y,width,height) — that is accepted, but a point array like [10, 20] or a string of \"x,y\" is not; JSON args built by an LLM with the wrong shape; copying options from a different library's drag API.","solutions":["Pass each endpoint as a selector string or as { x: number, y: number } with both fields numeric.","Convert other shapes yourself: arrays to { x: a[0], y: a[1] }, bbox objects already satisfy x/y.","Check the tab.drag() signature in the tool schema and match it exactly.","If coordinates come from computed values, validate typeof x === \"number\" && typeof y === \"number\" before the call."],"exampleFix":"// before\nawait tab.drag({ from: [120, 40], to: \"#drop\" });\n// after\nawait tab.drag({ from: { x: 120, y: 40 }, to: \"#drop\" });","handlingStrategy":"validation","validationCode":"function assertDragPoint(p: unknown): asserts p is string | { x: number; y: number } {\n  if (typeof p === \"string\") return;\n  if (p && typeof p === \"object\" && typeof (p as any).x === \"number\" && typeof (p as any).y === \"number\") return;\n  throw new TypeError(`drag endpoint must be selector string or {x,y}; got ${typeof p}`);\n}\nassertDragPoint(from); assertDragPoint(to);","typeGuard":"function isPoint(p: unknown): p is { x: number; y: number } {\n  return !!p && typeof p === \"object\" && typeof (p as any).x === \"number\" && typeof (p as any).y === \"number\";\n}","tryCatchPattern":"try {\n  await tab.drag({ from, to });\n} catch (err) {\n  if (err.message.includes(\"must be a selector string or\")) {\n    throw new Error(`bad drag args: from=${JSON.stringify(from)} to=${JSON.stringify(to)}`);\n  }\n  throw err;\n}","preventionTips":["Validate endpoint shapes at the call-site boundary","Convert arrays/other shapes to { x, y } before calling","Match the tool schema exactly when constructing args programmatically","Add unit tests for arg-building code paths"],"tags":["browser","drag","validation","arguments"],"backgroundTag":"invalid-argument-shape","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}