ElementResolutionError in ego-lite: Handling Transient and Permanent Resolution Failures

ElementResolutionError is a custom error class in ego-lite that classifies resolution failures as either transient (retryable) or permanent (fatal), enabling automation scripts to implement intelligent retry logic.

ElementResolutionError powers the element-resolution subsystem in ego-lite, an open-source browser automation framework maintained by CitroLabs. Defined in src/element-resolver.ts, this error provides granular failure classification that distinguishes between DOM elements that may appear after a short delay versus fundamentally broken or ambiguous selectors.

What is ElementResolutionError?

ElementResolutionError extends the native JavaScript Error class to signal specific failure modes during element resolution. According to the ego-lite source code, the runtime throws this error whenever it cannot locate or interact with a target element using the supplied selector or reference.

The class exposes a kind property that communicates whether the failure is recoverable. This design pattern allows calling code in src/driver/waits.ts and src/driver/locator.ts to implement polling loops that differentiate between temporary DOM states and permanent configuration errors.

The Two Kinds of ElementResolutionError

The error type discriminates between two distinct failure modes through its string-literal kind property.

Transient Errors (Retryable)

A transient error indicates that the element might become available if the script waits briefly. This kind covers scenarios where:

  • The element is still loading or rendering
  • The element exists in the DOM but is temporarily hidden
  • The DOM reference has become stale and needs refresh
  • Box-model data is not yet computed

The wait logic in src/driver/waits.ts specifically checks for err.kind === "transient" to continue polling rather than aborting the operation.

Permanent Errors (Non-Retryable)

A permanent error signals that retries would be wasteful because the selector itself is invalid or ambiguous. This kind triggers when:

  • The selector syntax is malformed or evaluates to an exception
  • The query matches multiple elements when exactly one is required
  • Required attributes are missing or immutable

Catching a permanent error allows scripts to fail fast with descriptive messaging rather than consuming resources on futile polling attempts.

Class Implementation in element-resolver.ts

The error class is implemented in src/element-resolver.ts with a straightforward constructor that accepts a message and kind discriminator:

export class ElementResolutionError extends Error {
  kind: "transient" | "permanent";
  
  constructor(message: string, kind: "transient" | "permanent") {
    super(message);
    this.name = "ElementResolutionError";
    this.kind = kind;
  }
}

This structure enables TypeScript's type narrowing and allows runtime inspection via instanceof checks.

Where ElementResolutionError is Raised

The ego-lite codebase raises ElementResolutionError in four primary contexts:

  • Selector parsing — Thrown as selectorResolutionError when a selector evaluates to an exception or matches an invalid number of elements
  • Reference look-up — Triggered when a numeric reference (@N) is unknown or its backend node is stale, typically returning transient for "Unknown ref" scenarios
  • AX role/name lookup — Returns transient for zero matches (element may appear) but permanent for ambiguous matches exceeding one element
  • Box-model extraction — Raises transient when an element lacks a usable box model, indicating the layout calculation is incomplete

Practical Error Handling Patterns

The following patterns demonstrate how to catch and respond to ElementResolutionError based on its kind.

Example: Conditional retry logic

import { ElementResolutionError } from "./element-resolver.js";

try {
  const { x, y } = await resolveElementCenter(cdp, sessionId, refMap, "#submit");
  // … use coordinates …
} catch (err) {
  if (err instanceof ElementResolutionError) {
    if (err.kind === "transient") {
      // Retry later – element may appear after a short delay
      await delay(200);
      return resolveElementCenter(cdp, sessionId, refMap, "#submit");
    }
    // Permanent – selector is wrong, abort with a helpful message
    console.error("Permanent resolution failure:", err.message);
  } else {
    throw err; // re‑throw unexpected errors
  }
}

Example: Integration with wait utilities

import { waitFor } from "./driver/waits.js";

await waitFor(async () => {
  try {
    await resolveElementCenter(cdp, sessionId, refMap, "role:button[name=Submit]");
    return true; // success
  } catch (e) {
    if (e instanceof ElementResolutionError && e.kind === "transient") {
      return false; // keep waiting
    }
    throw e; // permanent → stop waiting immediately
  }
});

Summary

  • ElementResolutionError is defined in src/element-resolver.ts and extends the native Error class
  • The error provides a kind property with two values: "transient" for retryable failures and "permanent" for fatal selector errors
  • Transient errors indicate the element may appear after a delay (loading, hidden, stale), while permanent errors indicate malformed selectors or ambiguous matches
  • The class is used throughout src/driver/waits.ts and src/driver/locator.ts to drive intelligent polling and failure logic
  • Callers should use instanceof ElementResolutionError and inspect err.kind to decide between retrying and aborting

Frequently Asked Questions

How do I properly catch ElementResolutionError in ego-lite?

Import the class from src/element-resolver.ts and use an instanceof check within your catch block. This ensures you only handle the specific resolution error while allowing other unexpected errors to propagate naturally.

What causes a transient ElementResolutionError?

Transient errors occur when the target element exists in the page logic but is not currently resolvable due to timing. Common causes include network delays preventing DOM updates, CSS animations hiding the element, or stale references requiring a refresh cycle.

Why would a selector trigger a permanent ElementResolutionError?

Permanent errors indicate that the selector itself is the problem. This includes syntax errors in the selector string, queries that resolve to multiple elements when only one is expected, or attempts to reference attributes that do not exist on the matched element.

Which ego-lite files should I examine to understand resolution error handling?

Study src/element-resolver.ts for the class definition, src/driver/waits.ts for retry-loop implementations, src/driver/locator.ts for locator-specific handling, and src/element-resolver.test.mjs for unit tests demonstrating expected behavior patterns.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →