CloakHQ/CloakBrowser · error · UnsupportedHumanizeSelectorError

Humanized selector ${selector} is not supported by the isola

Error message

Humanized selector ${selector} is not supported by the isolated-world resolver. Supported: CSS, text=, xpath=, getByTestId, getByPlaceholder, getByAltText, getByTitle, getByText, getByLabel, and a trailing .first()/.nth()/.last(). Not supported: getByRole, chained locators (a >> b, .filter(), .and(), .or()), frameLocator, :visible and :nth-match. Use a CSS or text= selector for this action, or humanize:false to fall back to Playwright.

What it means

The humanized (isolated-world) resolver only understands a subset of selector syntax: plain CSS, text=, xpath=, getByTestId/Placeholder/AltText/Title/Text/Label pseudo-locators, and a single trailing .first()/.nth()/.last(). When buildSnapshotJs runs the selector in the stealth world it returns an UNSUPPORTED status, and selectorSnapshot maps that to this error. It exists because the isolated-world resolver is a deliberately small JS evaluator that cannot parse Playwright's full locator engine (getByRole, chaining, filters, frameLocator, engine pseudo-selectors).

Source

Thrown at js/src/human/index.ts:181

  x = 0;
  y = 0;
  initialized = false;
}


// ============================================================================
// Canonical selector snapshot — isolated world only
// ============================================================================

async function selectorSnapshot(
  stealth: StealthEval | null,
  selector: string,
): Promise<SnapshotPayload> {
  if (!stealth) throw new StealthWorldUnavailableError();
  const { status, data } = await evalParsed(stealth, buildSnapshotJs(selector));
  if (status === OK && data) return data as SnapshotPayload;
  if (status === NOT_FOUND) throw new ElementNotAttachedError(selector);
  if (status === UNSUPPORTED) throw new UnsupportedHumanizeSelectorError(selector);
  throw new StealthEvaluationError(selector);
}


// ============================================================================
// Page-level patching
// ============================================================================

/**
 * Replace page methods with human-like implementations.
 */
function patchPage(page: Page, cfg: HumanConfig, cursor: CursorState): void {
  const originals = {
    click: page.click.bind(page),
    dblclick: page.dblclick.bind(page),
    hover: page.hover.bind(page),
    type: page.type.bind(page),
    fill: page.fill.bind(page),

View on GitHub (pinned to d6bad5de26)

Solutions

  1. Replace the locator with a plain CSS selector or text= selector for this action (e.g. 'button#submit' or 'text=Sign in').
  2. Use a supported pseudo-locator such as getByTestId('submit') or getByText('Sign in') with at most one trailing .first()/.nth(i)/.last().
  3. Pass humanize:false (or the equivalent option) on this call to fall back to normal Playwright locator resolution.
  4. Restructure chained locators (a >> b, .filter(), .and(), .or()) into a single more specific CSS/text selector, and resolve frame context in your own code instead of frameLocator.

Example fix

// before
await page.getByRole('button', { name: 'Sign in' }).humanPress('Enter');
// or: await page.locator('a >> text=Sign in').humanClick();

// after
await humanPress(page, 'text=Sign in', 'Enter');
// or pass humanize:false to fall back to Playwright's engine
await page.locator('button[name="signin"]').click({ humanize: false });
Defensive patterns

Strategy: validation

Validate before calling

import { isHumanizableSelector } from './human/selector';

if (!isHumanizableSelector(selector)) {
  // use a supported form or fall back to plain Playwright
  await page.locator(selector).click();
} else {
  await humanClick(page, selector);
}

Type guard

function isHumanizableSelector(sel: string): boolean {
  const supported = /^(?:[a-zA-Z[#.]\/][\s\S]*|text=|xpath=\/\/|getBy(TestId|Placeholder|AltText|Title|Text|Label)\()/;
  if (/getByRole|>>|\.filter\(|\.and\(|\.or\(|frameLocator|:visible|:nth-match/.test(sel)) return false;
  return supported.test(sel);
}

Try / catch

try {
  await humanPress(page, selector, 'Enter');
} catch (e) {
  if (e instanceof UnsupportedHumanizeSelectorError) {
    await page.locator(selector).press('Enter'); // humanize:false fallback
  } else throw e;
}

Prevention

When it happens

Trigger: Calling humanClear(), humanPress(), pressSequentially(), isChecked/checked, or isInput (anything routed through selectorSnapshot) with a selector like getByRole('button'), 'a >> text=Sign in', locator.filter({...}), .and()/.or(), a frameLocator-prefixed selector, ':visible', or ':nth-match'.

Common situations: Copy-pasting selectors from existing Playwright tests into humanized action calls; upgrading to a version where humanize:true became the default; using codegen-generated getByRole locators with the humanizer enabled.

Related errors


AI-assisted analysis of CloakHQ/CloakBrowser@d6bad5de26 (2026-08-28). Data as JSON: /api/errors/139accd99c4a86a9. Report an issue: GitHub.