Transient vs Permanent Element Resolution Failures in ego-lite: A Complete Guide

In ego-lite, element resolution failures are categorized as either transient (retryable) or permanent (non-retryable) via the kind property on ElementResolutionError, determining whether the automation runtime will retry the lookup or abort immediately.

When automating browser interactions with citrolabs/ego-lite, the framework must gracefully handle cases where DOM elements are not immediately available or cannot be located. The library distinguishes between temporary conditions that might resolve on their own and fundamental errors that will persist regardless of timing. This distinction is encapsulated in the ElementResolutionError class and its kind property, which drives retry logic throughout the browser automation pipeline.

Understanding ElementResolutionError and the kind Property

At the heart of ego-lite’s error handling lies the ElementResolutionError class defined in src/element-resolver.ts. This error object carries a kind property that accepts two string literals: "transient" or "permanent". The runtime inspects this property to determine whether a failed element lookup warrants a retry or should surface immediately as a hard failure.

The architecture separates these concerns to prevent unnecessary polling when success is impossible (permanent errors) while allowing robust waiting mechanisms for timing-sensitive operations (transient errors).

Transient Element Resolution Failures

Transient failures represent retryable conditions where the element might become available if the automation waits briefly. These errors signal that the lookup logic is sound, but the DOM state is not yet ready.

Common Causes of Transient Failures

According to the source code in src/element-resolver.ts, transient errors occur in three primary scenarios:

  • Missing box model: When resolveElementCenter detects that an element lacks a box model (indicating it has not rendered or is zero-sized), it throws a transient error at lines 55-62.
  • Zero locator matches: Role-based or selector-based locators that return no matches produce a transient failure at lines 46-50.
  • Unknown reference IDs: Attempts to resolve a ref ID that does not yet exist in the internal registry trigger transient errors at lines 73-76.

Runtime Behavior and Retry Logic

Callers implementing retry loops—such as waitForSelector or ref fallback mechanisms—automatically repeat the lookup when they catch a transient error. The driver code in src/driver/waits.ts explicitly checks for this condition at lines 505-506:

if (err instanceof ElementResolutionError && err.kind === "transient") {
  // Retry logic continues polling until timeout
}

This pattern allows ego-lite to wait for dynamic content, SPA navigation, or lazy-loaded elements without failing prematurely.

Permanent Element Resolution Failures

Permanent failures indicate non-retryable conditions where repeating the lookup would not change the outcome. These errors represent configuration problems, ambiguous selectors, or fundamental mismatches between the locator and the DOM structure.

Common Causes of Permanent Failures

The src/element-resolver.ts file identifies several permanent error conditions at specific line ranges:

  • Invalid selectors: Syntax errors or unevaluatable selector strings trigger permanent failures at lines 85-90.
  • Ambiguous matches: When a locator returns multiple elements but the operation requires a single unique match, the resolver throws a permanent error at lines 34-38.
  • Missing backend DOM node IDs: Accessibility (AX) nodes that lack a backendDOMNodeId cannot be mapped to actual DOM nodes, resulting in a permanent error at lines 58-62.

Why Permanent Errors Abort Immediately

Unlike transient errors, permanent failures propagate immediately to the calling agent. The runtime assumes that invalid selectors or ambiguous locators require code changes rather than additional wait time. For example, src/driver/locator.ts uses this distinction to decide whether to attempt fallback strategies or abort the operation entirely.

Code Examples: Handling Each Failure Type

The following examples from the ego-lite source demonstrate how the framework categorizes and handles these errors.

Throwing a transient error when the box model is missing:

// src/element-resolver.ts
if (!boxModel || boxModel.content.length < 8) {
  throw new ElementResolutionError(
    "Element has no box model (not rendered or zero-sized)",
    "transient",
  );
}

Throwing a permanent error for ambiguous or invalid selectors:

// src/element-resolver.ts
if (/\bmatched \d+ elements\b/.test(message)) {
  // Multiple matches when single required
  return new ElementResolutionError(message, "permanent");
}
return new ElementResolutionError(
  `Invalid selector: ${selector}: ${message}`,
  "permanent",
);

Driver-level retry logic distinguishing error kinds:

// src/driver/waits.ts
try {
  await waitForSomething();
} catch (err) {
  if (err instanceof ElementResolutionError && err.kind === "transient") {
    // Continue retry loop
  } else {
    // Abort immediately for permanent failures
    throw err;
  }
}

Summary

  • Transient failures in ego-lite indicate retryable conditions such as missing box models, zero locator matches, or unknown reference IDs, allowing the runtime to poll until success or timeout.
  • Permanent failures represent non-retryable errors including invalid selectors, ambiguous multiple matches, or missing backend DOM node IDs, causing immediate abortion of the operation.
  • The ElementResolutionError class in src/element-resolver.ts encapsulates this distinction via its kind property, which src/driver/waits.ts inspects at lines 505-506 to determine retry eligibility.
  • Higher-level helpers like those in src/driver/locator.ts use this categorization to decide between fallback strategies and hard failures.

Frequently Asked Questions

What triggers a transient element resolution failure in ego-lite?

Transient failures occur when an element cannot be located due to timing or rendering state rather than configuration errors. Specific triggers include elements lacking a box model (not yet rendered), locators returning zero matches, or attempts to resolve unknown reference IDs. These conditions are implemented in src/element-resolver.ts and allow the runtime to retry the lookup.

When does ego-lite throw a permanent element resolution error?

Permanent errors occur when the lookup mechanism itself is flawed or ambiguous. This includes syntactically invalid selector strings, locators matching multiple elements when a single unique element is required, and accessibility nodes that cannot be mapped to DOM nodes due to missing backendDOMNodeId values. These errors abort immediately without retry.

How does ego-lite decide whether to retry a failed element lookup?

The decision occurs in driver code such as src/driver/waits.ts, which catches ElementResolutionError instances and inspects the kind property. If kind === "transient", the driver continues its retry loop; otherwise, it re-throws the error immediately. This pattern prevents wasted cycles on permanently unresolvable conditions.

Can a transient error become permanent in ego-lite?

No, the classification is determined at the moment of error creation based on the specific failure condition. However, a transient error that persists beyond the configured timeout will eventually cause the operation to fail, though it remains classified as transient. The error type does not mutate; instead, the runtime simply stops retrying and surfaces the last transient error encountered.

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 →