vercel-labs/agent-browser · error

Accessibility audit world has a locked axe property

Error message

Accessibility audit world has a locked axe property

What it means

Thrown by the JavaScript prelude that agent-browser injects into its private isolated world (named __agent_browser_a11y_4_12_1__, one per frame) before evaluating the vendored axe-core 4.12.1 build. The prelude captures the world-local descriptor of window.axe; if a previous script in that same named world already defined 'axe' as non-configurable, redefining it would throw or invoke a foreign accessor, so the audit aborts instead of corrupting the capture. It reaches the CLI user as an 'Evaluation error: ...' from the a11y command.

Source

Thrown at cli/src/native/a11y/mod.rs:45

    tags.map(|tags| {
        tags.split(',')
            .map(str::trim)
            .filter(|tag| !tag.is_empty())
            .collect()
    })
    .unwrap_or_default()
}

fn private_engine_setup() -> String {
    format!(
        r#"const previousAxe = Object.getOwnPropertyDescriptor(window, 'axe');
  let agentAxe;
  try {{
    // This runs in an isolated world, never the page's JavaScript world. Guard
    // the world-local descriptor anyway so axe's UMD assignment cannot invoke
    // an accessor if another audit helper reused this world name.
    if (previousAxe && !previousAxe.configurable) {{
      throw new Error('Accessibility audit world has a locked axe property');
    }}
    Object.defineProperty(window, 'axe', {{
      value: undefined,
      writable: true,
      enumerable: previousAxe ? previousAxe.enumerable : false,
      configurable: true,
    }});
    // The vendored UMD build exports through this lexical CommonJS module.
    // Hide world-local AMD loaders so evaluating axe cannot register modules
    // outside the private CommonJS export.
    const module = {{ exports: {{}} }};
    const define = undefined;
    {axe_js}
    agentAxe = module.exports;
  }} finally {{
    // axe-core also assigns window.axe in browsers. Restore this isolated world
    // exactly after capturing our private export.
    if (previousAxe) {{

View on GitHub (pinned to 548b159b30)

Solutions

  1. Re-navigate the page (e.g. 'agent-browser open <url>') — navigation destroys the frame's isolated worlds, so the next audit gets a clean world
  2. Restart the browser session: 'agent-browser close' then relaunch with 'agent-browser open'
  3. Upgrade agent-browser — the world name embeds the axe version (__agent_browser_a11y_4_12_1__), so a version bump gets a fresh world name
  4. Stop injecting other axe-core scripts into agent-browser's __agent_browser_a11y_*__ isolated worlds

Example fix

// before
agent-browser a11y --session stale   # Evaluation error: ... locked axe property

// after
agent-browser close --session stale
agent-browser open https://app --session stale
agent-browser a11y --session stale
Defensive patterns

Strategy: retry

Try / catch

// In your orchestration code (running the CLI):
// try { run a11y } catch (err) {
//   if (String(err).includes('locked axe property')) {
//     re-navigate or restart the session, then retry the audit once;
//   } else throw;
// }

Prevention

When it happens

Trigger: Running 'agent-browser a11y' on a frame where another audit helper (browser extension, user-injected script, tooling that reuses the world name) has left a non-configurable window.axe in the isolated world; repeated audits on a long-lived frame after an earlier audit crashed mid-setup and left a locked descriptor.

Common situations: Mixing agent-browser a11y audits with other axe-based tooling attached to the same Chrome instance; very long-lived sessions that survived many navigations-less audits; running an older agent-browser side by side with a newer one whose world names collide.


AI-assisted analysis of vercel-labs/agent-browser@548b159b30 (2026-08-16). Data as JSON: /api/errors/254ef17ebaf85771. Report an issue: GitHub.