can1357/oh-my-pi · error · ToolError

Unsupported selector prefix. Use CSS or puppeteer query hand

Error message

Unsupported selector prefix. Use CSS or puppeteer query handlers (aria/, text/, xpath/, pierce/). Got: ${selector}

What it means

normalizeSelector rejects selectors starting with the legacy `p-` prefix unless they match a recognized legacy form. These `p-` handlers were the old puppeteer-style prefixes; unrecognized `p-...` selectors are refused with a pointer to the supported handlers (aria/, text/, xpath/, pierce/) or plain CSS.

Source

Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:290

	}): Promise<HTTPResponse | null>;
	id(n: number): Promise<ActionableHandle>;
	ref(id: string): Promise<ActionableHandle>;
}

export function normalizeSelector(selector: string): string {
	assertSelectorString(selector);
	if (!selector) return selector;
	if (
		!SELECTOR_HANDLER_PREFIXES.some(prefix => selector.startsWith(prefix)) &&
		PLAYWRIGHT_ONLY_SELECTOR_RE.test(selector)
	) {
		throw new ToolError(
			`Playwright-only selector ${JSON.stringify(selector)} is not supported by the browser tool. ` +
				`Use a puppeteer text selector ("text/Allow all"), an aria selector ("aria/Name"), CSS, or "xpath/...".`,
		);
	}
	if (selector.startsWith("p-") && !LEGACY_SELECTOR_PREFIXES.some(prefix => selector.startsWith(prefix))) {
		throw new ToolError(
			`Unsupported selector prefix. Use CSS or puppeteer query handlers (aria/, text/, xpath/, pierce/). Got: ${selector}`,
		);
	}
	if (selector.startsWith("p-text/")) return `text/${selector.slice("p-text/".length)}`;
	if (selector.startsWith("p-xpath/")) return `xpath/${selector.slice("p-xpath/".length)}`;
	if (selector.startsWith("p-pierce/")) return `pierce/${selector.slice("p-pierce/".length)}`;
	if (selector.startsWith("p-aria/")) {
		const rest = selector.slice("p-aria/".length);
		const nameMatch = rest.match(/\[\s*name\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\]]+))\s*\]/);
		const name = nameMatch?.[1] ?? nameMatch?.[2] ?? nameMatch?.[3];
		if (name) return `aria/${name.trim()}`;
		return `aria/${rest}`;
	}
	return selector;
}

function isInteractiveNode(node: SerializedAXNode): boolean {
	if (INTERACTIVE_AX_ROLES.has(node.role)) return true;

View on GitHub (pinned to 9690622007)

Solutions

  1. Drop the `p-` prefix: `p-aria/Name` → `aria/Name`, `p-text/...` → `text/...`.
  2. For text/xpath/pierce the legacy `p-` forms are auto-rewritten — use the bare form directly to avoid relying on the alias.
  3. Use plain CSS when no handler is needed.
  4. Update stored prompts/playbooks that still emit `p-` selectors.

Example fix

// before
tab.click("p-text/Allow all");
// after
tab.click("text/Allow all");
Defensive patterns

Strategy: validation

Validate before calling

if (selector.startsWith("p-")) {
  selector = selector.replace(/^p-(text|xpath|pierce)\//, "$1/"); // migrate legacy prefix
}

Prevention

When it happens

Trigger: Passing selectors like `p-role/button`, `p-css=...`, or any `p-`-prefixed string that is not one of the migrated legacy forms (`p-text/`, `p-xpath/`, `p-pierce/`, etc.).

Common situations: Older prompts/scripts/agent traces that used the deprecated `p-` selector dialect; mixed-up prefixes like `p-aria/` after the migration renamed them to bare `aria/`.

Related errors


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