CloakHQ/CloakBrowser · error · StealthWorldUnavailableError

Humanized DOM read requires an active isolated world

Error message

Humanized DOM read requires an active isolated world

What it means

Humanized scrolling needs to read window.scrollY and the document's scroll metrics from inside the isolated (stealth) world via getWorld(page).evaluate. If no world is registered on the page (getWorld returns undefined), readScrollState throws this error before attempting any evaluation. It signals the humanizer was used on a page where its isolated world was never installed.

Source

Thrown at js/src/human/scroll.ts:48

function isInViewport(
  bounds: ElementBounds,
  viewportHeight: number,
  cfg: HumanConfig,
): boolean {
  const topEdge = bounds.y;
  const bottomEdge = bounds.y + bounds.height;
  const zoneTop = viewportHeight * cfg.scroll_target_zone[0];
  const zoneBottom = viewportHeight * cfg.scroll_target_zone[1];
  return topEdge >= zoneTop && bottomEdge <= zoneBottom;
}

const SCROLL_JS =
  '(() => { const e = document.scrollingElement || document.documentElement;' +
  ' return { y: window.scrollY, maxY: Math.max(0, e.scrollHeight - e.clientHeight) }; })()';

async function readScrollState(page: Page): Promise<{ y: number; maxY: number }> {
  const world = getWorld(page);
  if (!world) throw new StealthWorldUnavailableError();
  try {
    const state = await world.evaluate(SCROLL_JS);
    if (
      !state || typeof state !== 'object' ||
      typeof state.y !== 'number' || typeof state.maxY !== 'number'
    ) {
      throw new StealthEvaluationError('<scroll-state>');
    }
    return state;
  } catch (error) {
    if (error instanceof StealthEvaluationError) throw error;
    throw new StealthEvaluationError('<scroll-state>');
  }
}

async function smoothWheel(raw: RawMouse, delta: number, cfg: HumanConfig): Promise<void> {
  const absD = Math.abs(delta);
  const sign = delta > 0 ? 1 : -1;

View on GitHub (pinned to d6bad5de26)

Solutions

  1. Create the page through the library's provided API (browser.newPage wrapper / withStealth / init call) so the isolated world is injected.
  2. Ensure humanized scroll-dependent actions are only used while the page/context the world was installed on is still open.
  3. Re-initialize the world on the current page with the library's setup function before calling human actions.
  4. If you don't need humanization for this action, call the plain Playwright scrollIntoView/click instead.

Example fix

// before
const page = await context.newPage(); // no isolated world installed
await scrollToElement(page, raw, 'text=Footer');

// after
const page = await openHumanPage(browser); // library wrapper that installs the world
await scrollToElement(page, raw, 'text=Footer');
Defensive patterns

Strategy: validation

Validate before calling

import { getWorld } from './human/world';
if (!getWorld(page)) throw new Error('Open the page via openHumanPage() before humanized actions');
await scrollToElement(page, raw, selector);

Type guard

import { getWorld } from './human/world';
function hasHumanWorld(page: Page): boolean {
  return getWorld(page) !== undefined;
}

Try / catch

try {
  await scrollToElement(page, raw, selector);
} catch (e) {
  if (e instanceof StealthWorldUnavailableError) {
    const page2 = await openHumanPage(browser); // re-create with world
    await scrollToElement(page2, raw, selector);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling scrollToElement, scrollByHuman, or any humanized action that scrolls (click/press paths calling readScrollState) on a Page that was not set up with the stealth/isolated-world injection (e.g. a plain page.create() without the library's page patching, or after the world was torn down).

Common situations: Creating pages manually instead of through the library's browser wrapper; using humanized APIs after context.close()/world cleanup; running against a remotely connected browser (playwright.chromium.connectTo) where the injection step was skipped; version upgrade changing how pages get patched.

Related errors


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