CloakHQ/CloakBrowser · error · InvalidOperationException

Element lost after scrolling into view

Error message

Element lost after scrolling into view

What it means

InvalidOperationException thrown at the end of HumanScrollIntoViewAsync: after performing the humanized scroll and settle delay, re-querying the element's box returns null. The element disappeared from the DOM during the scroll.

Source

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

        {
            int overshootPx = (int)(Math.Round(HumanRandom.RandRange(cfg.ScrollOvershootPx)) * direction);
            await SmoothWheelAsync(raw, overshootPx, cfg).ConfigureAwait(false);
            await HumanRandom.SleepMsAsync(HumanRandom.RandRange(cfg.ScrollSettleDelay)).ConfigureAwait(false);
            int corrections = HumanRandom.RandIntRange((1, 2));
            for (int c = 0; c < corrections; c++)
            {
                int corrDelta = (int)(Math.Round(HumanRandom.Rand(40, 80)) * -direction);
                await SmoothWheelAsync(raw, corrDelta, cfg).ConfigureAwait(false);
                await HumanRandom.SleepMsAsync(HumanRandom.Rand(100, 250)).ConfigureAwait(false);
            }
        }

        // Settle.
        await HumanRandom.SleepMsAsync(HumanRandom.RandRange(cfg.ScrollSettleDelay)).ConfigureAwait(false);

        box = await getBox().ConfigureAwait(false);
        if (box == null)
            throw new InvalidOperationException("Element lost after scrolling into view");

        return new ScrollResult(box.Value, cursorX, cursorY, true);
    }
}

View on GitHub (pinned to d6bad5de26)

Solutions

  1. Use a stable selector that survives re-renders (data-testid rather than index-based nth-child)
  2. Re-locate the element and retry the scroll after the lazy content settles
  3. Wait for DOM stability after scroll-triggering loads before asserting on the element
  4. If the node is intentionally replaced, target the container and scroll to that instead

Example fix

// before
await page.HumanScrollIntoViewAsync("ul > li:nth-child(900)");

// after
await page.HumanScrollIntoViewAsync("ul[data-testid='results']");
await page.WaitForSelectorAsync("ul > li:nth-child(900)");
Defensive patterns

Strategy: retry

Validate before calling

// cannot fully validate; ensure stable selector exists
var count = await page.Locator(selector).CountAsync(); // use resilient selector

Try / catch

catch (InvalidOperationException e) when (e.Message.Contains("lost after scrolling")) { re-locate by stable selector and retry once }

Prevention

When it happens

Trigger: Scrolling to an element that gets removed/re-rendered as a side effect of scrolling — lazy loaders that replace nodes, infinite lists that recycle DOM nodes, or SPAs that re-mount components on scroll events.

Common situations: Virtualized/infinite-scroll lists, content lazy-loaded on scroll that swaps the target node, or the page navigating away mid-scroll.

Related errors


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