CloakHQ/CloakBrowser · error · InvalidOperationException
Viewport size not available
Error message
Viewport size not available
What it means
InvalidOperationException from HumanScrollIntoViewAsync when neither page.ViewportSize nor the live window dimensions (via GetLiveWindowSizeAsync) yield a usable viewport height (null or Height == 0). Without a viewport height the scroller cannot compute zones or distances.
Source
Thrown at dotnet/src/CloakBrowser/Human/HumanScroll.cs:107
/// overshoot behavior.
/// </summary>
/// <returns>(box, cursorX, cursorY, didScroll) - didScroll is false when the
/// element was already in the viewport.</returns>
public static async Task<ScrollResult> HumanScrollIntoViewAsync(
IRawScrollPage page,
IRawMouse raw,
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;View on GitHub (pinned to d6bad5de26)
Solutions
- Ensure the page has fully navigated to real content before humanized scrolling
- For headless runs, set an explicit Viewport in BrowserContextOptions so ViewportSize is populated
- Check the page isn't closing (IsClosed) before issuing scroll calls
- Verify the stealth scripts are installed (they provide the live-dimension fallback)
Example fix
// before
var ctx = await browser.NewContextAsync(); // NoViewport default in headed mode
// after
var ctx = await browser.NewContextAsync(new BrowserContextOptions { Viewport = new(1280, 800) }); Defensive patterns
Strategy: validation
Validate before calling
if (page.ViewportSize is null or { Height: 0 }) await page.SetViewportSizeAsync(1280, 800); Try / catch
catch (InvalidOperationException e) when (e.Message == "Viewport size not available") { /* set viewport, retry */ } Prevention
- Set explicit viewport for headless runs
- Don't call scroll APIs on closing/blank pages
- Wait for a real navigation before humanizing
When it happens
Trigger: Calling humanized scroll-into-view when the page has no ViewportSize (headed no_viewport mode) AND the stealth-world viewport probe also returns null or a zero-height object — e.g. navigation happening concurrently, or the stealth isolated world not being installed.
Common situations: Headed launches with NoViewport = true where the page is closing/closed, about:blank with no layout, race between navigation and humanize calls, or a stripped-down stealth script that doesn't expose the viewport probe.
Related errors
- CLOAKBROWSER_BINARY_PATH set to '${localOverride}' but file
- Viewport size not available
- Humanized DOM read requires an active isolated world
- Element not found while scrolling into view
- Element lost after scrolling into view
AI-assisted analysis of CloakHQ/CloakBrowser@d6bad5de26 (2026-08-28).
Data as JSON: /api/errors/6b127b2eec8a2f81.
Report an issue: GitHub.