can1357/oh-my-pi · warning

mouse action must be a string

Error message

mouse action must be a string

What it means

In the debug server's 'mouse' command handler, 'request.action' defaults to 'click' when undefined, but if it is present and not a string, this error is thrown before any mouse sequence is injected. The debug protocol only accepts known string actions (e.g. click, move, drag).

Source

Thrown at packages/tui/src/debug-server.ts:370

			case "keys": {
				if (typeof request.keys !== "string") throw new Error("keys must be a string");
				const parsed = parseKeyTokens(request.keys);
				for (const sequence of parsed.sequences) this.#tui.injectDebugInput(sequence);
				return { ok: true, injected: parsed.events };
			}
			case "bytes":
				if (typeof request.data !== "string") throw new Error("data must be a string");
				this.#tui.injectDebugInput(request.data);
				return { ok: true };
			case "paste":
				if (typeof request.text !== "string") throw new Error("text must be a string");
				this.#tui.injectDebugInput(`\x1b[200~${request.text}\x1b[201~`);
				return { ok: true };
			case "mouse": {
				if (typeof request.x !== "number" || typeof request.y !== "number")
					throw new Error("mouse x and y must be numbers");
				const action = request.action === undefined ? "click" : request.action;
				if (typeof action !== "string") throw new Error("mouse action must be a string");
				this.#tui.injectDebugInput(mouseSequence(request.x, request.y, action));
				return { ok: true };
			}
			case "quit":
				return { ok: true };
			default:
				return { ok: false, error: `unknown op ${request.op}` };
		}
	}

	#node(component: Component): TuiDebugTreeNode {
		const children = componentChildren(component);
		const focusable = isFocusable(component);
		return {
			kind: componentKind(component),
			...(component.debugId === undefined ? {} : { id: component.debugId }),
			...(focusable ? { focusable: true, focused: component === this.#tui.getFocused() } : {}),
			...(children.length === 0 ? {} : { children: children.map(child => this.#node(child)) }),

View on GitHub (pinned to 9690622007)

Solutions

  1. Send 'action' as a string (or omit it entirely to get the 'click' default).
  2. Map numeric enum actions to their string names on the client before sending.
  3. Ensure the action is a valid action name accepted by mouseSequence (e.g. "click").
  4. Remove the 'action': null field explicitly rather than sending null.

Example fix

// before
await send({ method: "mouse", x: 10, y: 20, action: MouseButton.Left }); // number
// after
await send({ method: "mouse", x: 10, y: 20, action: "click" }); // or omit action
Defensive patterns

Strategy: validation

Validate before calling

const ACTIONS = new Set(["click", "move", "drag"]);
if (typeof action === "string" && ACTIONS.has(action)) {
  await send({ method: "mouse", x, y, action });
} // or omit action to default to "click"

Type guard

function isMouseAction(v: unknown): v is string {
  return typeof v === "string";
}

Try / catch

try {
  await send({ method: "mouse", x, y, action });
} catch (err) {
  if (err instanceof Error && err.message === "mouse action must be a string") {
    console.error(`invalid mouse action: ${JSON.stringify(action)}`);
  } else throw err;
}

Prevention

When it happens

Trigger: Sending a debug 'mouse' request where 'action' is a number, boolean, null, or an object — e.g. {"method":"mouse","x":10,"y":20,"action":1} or "action":null. Note explicit null is NOT treated as undefined, so it fails.

Common situations: Clients that serialize the action field from an enum as a number; passing a null from a JS client (null !== undefined); typos producing undefined is fine but a mistyped variable holding a non-string slips through.

Related errors


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