CloakHQ/CloakBrowser · error · StealthEvaluationError

Isolated-world DOM evaluation failed for "<scroll-state>"

Error message

Isolated-world DOM evaluation failed for "<scroll-state>"

What it means

After successfully evaluating the scroll-state script in the isolated world, readScrollState validates the returned shape: it must be an object with numeric y and maxY (from { y: window.scrollY, maxY: scrollHeight - clientHeight }). If the payload fails that validation (or the evaluate result is falsy), this error is thrown, indicating the document didn't yield usable scroll metrics.

Source

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

  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;
  let sent = 0;
  while (sent < absD) {
    const stepSize = rand(20, 40);
    const chunk = Math.min(stepSize, absD - sent);
    await raw.wheel(0, Math.round(chunk) * sign);
    sent += chunk;
    await sleep(rand(8, 20));

View on GitHub (pinned to d6bad5de26)

Solutions

  1. Wait for a real document: await page.waitForLoadState('domcontentloaded') (or 'load') before the human action.
  2. Ensure the target URL actually loaded (check page.url() for chrome-error:// or about:blank after failed navigations).
  3. If operating on special documents, scroll explicitly on the target element (locator.scrollIntoViewIfNeeded) instead of the humanized page-level scroll path.
  4. Verify no page.route/content interception is replacing the document with non-HTML content.

Example fix

// before
await page.goto(url); // navigation may have failed silently
await scrollToElement(page, raw, 'text=Next');

// after
await page.goto(url, { waitUntil: 'domcontentloaded' });
if (page.url().startsWith('chrome-error')) throw new Error('nav failed');
await scrollToElement(page, raw, 'text=Next');
Defensive patterns

Strategy: validation

Validate before calling

await page.waitForLoadState('domcontentloaded');
if (/^(about:blank|chrome-error)/.test(page.url())) throw new Error('No usable document');
await scrollToElement(page, raw, selector);

Type guard

function isUsableScrollState(s: unknown): s is { y: number; maxY: number } {
  return !!s && typeof s === 'object' &&
    typeof (s as any).y === 'number' && typeof (s as any).maxY === 'number';
}

Try / catch

try {
  await scrollToElement(page, raw, selector);
} catch (e) {
  if (e instanceof StealthEvaluationError && e.message.includes('<scroll-state>')) {
    await page.locator(selector).scrollIntoViewIfNeeded(); // non-humanized fallback
  } else throw e;
}

Prevention

When it happens

Trigger: world.evaluate(SCROLL_JS) resolves but returns null/undefined or an object where y or maxY is not a number — typically on exotic documents (e.g. an XML document, about:blank, a page whose scrollingElement/documentElement produce undefined metrics), or when serialization drops the fields.

Common situations: Calling humanized scroll/click on about:blank, the chrome-error page after a failed navigation, an iframe-less SVG/XML document, or before DOMContentLoaded when documentElement metrics are not yet numeric.

Related errors


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