CloakHQ/CloakBrowser · error · ElementNotAttachedError
ElementNotAttachedError: {selector}
Error message
ElementNotAttachedError: {selector} What it means
ElementNotAttachedError mapped from StealthStatus.NotFound in LocatorHumanizer.SnapshotAsync: the stealth DOM snapshot for the selector found no matching element, so the humanized locator action (check, uncheck, click, focus...) aborts.
Source
Thrown at dotnet/src/CloakBrowser/Wrappers/LocatorHumanizer.cs:20
using Microsoft.Playwright;
namespace CloakBrowser.Wrappers;
/// <summary>Humanized locator actions. Direct page.Locator selectors use the canonical isolated DOM world; unknown locator shapes retain the legacy Playwright path.</summary>
internal static class LocatorHumanizer
{
private static double RemainingMs(double deadline) => Actionability.RemainingMs(deadline);
private static async Task<IsolatedWorld> WorldAsync(HumanCursor cursor) =>
await cursor.GetStealthAsync().ConfigureAwait(false) ?? throw new StealthWorldUnavailableError();
internal static async Task<StealthSnapshot> SnapshotAsync(HumanCursor cursor, string selector)
{
var (status, snapshot) = await StealthDom.SnapshotAsync(await WorldAsync(cursor).ConfigureAwait(false), selector).ConfigureAwait(false);
return status switch
{
StealthStatus.Ok when snapshot != null => snapshot.Value,
StealthStatus.NotFound => throw new ElementNotAttachedError(selector),
StealthStatus.Unsupported => throw new UnsupportedHumanizeSelectorError(selector),
_ => throw new StealthEvaluationError(selector),
};
}
internal static async Task EnsureActionableAsync(HumanCursor cursor, string selector,
IReadOnlySet<string> checks, double timeout, bool force)
{
var world = await WorldAsync(cursor).ConfigureAwait(false);
await Actionability.EnsureActionableAsync(cursor.Page, selector, checks, timeout, force, world).ConfigureAwait(false);
}
private static async Task<BoundingBox?> GetBoxAsync(
IsolatedWorld world, string selector, double timeoutMs)
{
double deadline = System.Environment.TickCount64 + System.Math.Max(0, timeoutMs);
var result = await StealthDom.BoxAsync(world, selector).ConfigureAwait(false);
while ((result.Status == StealthStatus.NotFound || result.Status == StealthStatus.EvaluationFailed)View on GitHub (pinned to d6bad5de26)
Solutions
- Wait for the element to be attached/visible before the humanized action (locator.WaitForAsync)
- Verify the selector matches (count == 1) with a debug query
- Handle auth/redirects so the expected page is actually loaded
- Retry after content settles in dynamic SPAs
Example fix
// before
await page.Locator("#agree").HumanCheckAsync();
// after
await page.Locator("#agree").WaitForAsync(new() { State = WaitForSelectorState.Visible });
await page.Locator("#agree").HumanCheckAsync(); Defensive patterns
Strategy: try-catch
Validate before calling
await page.Locator(selector).WaitForAsync(new() { State = WaitForSelectorState.Attached }); Try / catch
catch (ElementNotAttachedError) { await page.WaitForSelectorAsync(selector); retry once; } Prevention
- Wait for the element before humanized locator actions
- Verify selectors match after auth flows
- Use stable data-testid attributes
When it happens
Trigger: Humanized Check/Uncheck/move on a selector with no attached match within the snapshot — element not yet rendered, wrong selector, or element removed before the call.
Common situations: SPA late rendering, selector typos, elements behind auth/redirects so the page content differs from expectation, virtualized lists.
Related errors
- Humanized selector ${selector} is not supported by the isola
- Element not found while scrolling into view
- ElementNotAttachedError: {sourceSelector}
- ElementNotAttachedError: {targetSelector}
- Element not found while scrolling into view
AI-assisted analysis of CloakHQ/CloakBrowser@d6bad5de26 (2026-08-28).
Data as JSON: /api/errors/df50eb87cb69ec17.
Report an issue: GitHub.