CloakHQ/CloakBrowser · error · InvalidOperationException

Element not found while scrolling into view

Error message

Element not found while scrolling into view

What it means

InvalidOperationException thrown when the element's bounding box (via the getBox delegate) returns null at the start of HumanScrollIntoViewAsync. The element to scroll to does not currently exist in the DOM.

Source

Thrown at dotnet/src/CloakBrowser/Human/HumanScroll.cs:114

        Func<Task<BoundingBox?>> getBox,
        double cursorX, double cursorY,
        HumanConfig cfg)
    {
        var viewport = page.ViewportSize;
        if (viewport == null)
            // Headed launches default to no_viewport so the page tracks the real OS
            // window; ViewportSize is then null. Fall back to the live window
            // dimensions so humanize works headed (the stealth-relevant mode).
            viewport = await page.GetLiveWindowSizeAsync().ConfigureAwait(false);
        if (viewport == null || viewport.Value.Height == 0)
            throw new InvalidOperationException("Viewport size not available");

        int viewportHeight = viewport.Value.Height;
        int viewportWidth = viewport.Value.Width;

        var box = await getBox().ConfigureAwait(false);
        if (box == null)
            throw new InvalidOperationException("Element not found while scrolling into view");

        if (IsInViewport(box.Value, viewportHeight, cfg))
            return new ScrollResult(box.Value, cursorX, cursorY, false);

        // Already fully visible but off-center, with the page pinned at the boundary
        // in the needed direction: scrolling can't help, so don't waste the budget.
        bool fullyVisible = box.Value.Y >= 0 && box.Value.Y + box.Value.Height <= viewportHeight;
        if (fullyVisible)
        {
            double zoneMid = viewportHeight * (cfg.ScrollTargetZone.Min + cfg.ScrollTargetZone.Max) / 2;
            bool needUp = box.Value.Y + box.Value.Height / 2 < zoneMid;
            var scroll = await page.GetScrollStateAsync().ConfigureAwait(false);
            if (scroll != null && (needUp ? scroll.Value.Y <= 0 : scroll.Value.Y >= scroll.Value.MaxY))
                return new ScrollResult(box.Value, cursorX, cursorY, false);
        }

        // Move cursor into scroll area.
        double scrollAreaX = Math.Round(viewportWidth * HumanRandom.Rand(0.3, 0.7));

View on GitHub (pinned to d6bad5de26)

Solutions

  1. Wait for the element (WaitForSelectorAsync, state Attached or Visible) before scrolling
  2. Fix the selector; verify it matches with a quick query
  3. For virtualized lists, scroll the container first so the row is rendered
  4. Retry after navigation settles if a click just caused a page change

Example fix

// before
await page.HumanScrollIntoViewAsync("#footer");

// after
await page.WaitForSelectorAsync("#footer");
await page.HumanScrollIntoViewAsync("#footer");
Defensive patterns

Strategy: try-catch

Validate before calling

await page.WaitForSelectorAsync(selector, new() { State = WaitForSelectorState.Attached });

Try / catch

catch (InvalidOperationException e) when (e.Message.Contains("not found while scrolling")) { await page.WaitForSelectorAsync(selector); retry once; }

Prevention

When it happens

Trigger: Calling humanized scroll APIs with a selector that matches no attached element, or where the element is detached between the caller's own wait and the internal getBox evaluation.

Common situations: Selector typos, elements removed by React/Vue re-renders, lists virtualized so the row isn't in the DOM yet, or scrolling immediately after a click that triggers navigation.

Related errors


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