CloakHQ/CloakBrowser · error · ActionabilityError

timeout

timeout

Error message

timeout

What it means

The actionability polling loop in EnsureActionableAsync ran out of time before even one DOM check completed, and no earlier element error was recorded to rethrow. It means the deadline elapsed with zero successful StealthDom.ActionableAsync evaluations — the element state was never assessed.

Source

Thrown at dotnet/src/CloakBrowser/Human/Actionability.cs:158

        IsolatedWorld? stealth = null)
    {
        if (force)
            return;
        if (stealth == null)
            throw new StealthWorldUnavailableError();

        double deadline = NowMs() + timeoutMs;
        int attempt = 0;
        Exception? lastError = null;

        while (true)
        {
            double remainingMs = Math.Max(0, deadline - NowMs());
            if (remainingMs <= 0)
            {
                if (lastError != null)
                    throw lastError;
                throw new ActionabilityError(selector, "timeout", "timeout expired before first check");
            }

            try
            {
                var (status, snapshot) = await StealthDom.ActionableAsync(
                    stealth, selector).ConfigureAwait(false);
                if (status == StealthStatus.Unsupported)
                    throw new UnsupportedHumanizeSelectorError(selector);
                if (status == StealthStatus.EvaluationFailed)
                    throw new StealthEvaluationError(selector);
                if (status == StealthStatus.NotFound)
                    throw new ElementNotAttachedError(selector);
                if (status != StealthStatus.Ok || snapshot == null)
                    throw new StealthEvaluationError(selector);

                var value = snapshot.Value;
                if (checks.Contains("visible") && !value.Visible)
                    throw new ElementNotVisibleError(selector);

View on GitHub (pinned to d6bad5de26)

Solutions

  1. Increase timeoutMs to a realistic value (hundreds of ms to seconds).
  2. Ensure the page is settled (not navigating) before starting actionability checks.
  3. Profile the isolated-world round-trip time; if it routinely exceeds the timeout, fix browser performance or headless-mode issues.
  4. If an earlier element error exists it is rethrown instead — address that underlying error first.

Example fix

// before
await Actionability.EnsureActionableAsync(page, selector, stealth, timeoutMs: 5);

// after
await Actionability.EnsureActionableAsync(page, selector, stealth, timeoutMs: 5000);
Defensive patterns

Strategy: retry

Validate before calling

if (timeoutMs < 250) throw new ArgumentOutOfRangeException(nameof(timeoutMs), "allow at least one DOM round-trip");

Try / catch

catch (ActionabilityError ex) when (ex.Code == "timeout")
{
    await Task.Delay(200);
    await Actionability.EnsureActionableAsync(page, selector, stealth, timeoutMs: Math.Max(timeoutMs * 4, 5000));
}

Prevention

When it happens

Trigger: Calling EnsureActionableAsync with a timeoutMs too small for even one round-trip to the isolated world (remainingMs <= 0 on the first iteration with lastError == null), or the browser/CDP connection being so slow the first check never completes.

Common situations: Passing timeoutMs of 0 or a few milliseconds; overloaded browser or CPU-starved worker making the first CDP evaluation exceed the budget; navigating while the check starts; unit tests with artificially tiny timeouts.

Understand the failure class

Related errors


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