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
- 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
- Restart the browser session: 'agent-browser close' then relaunch with 'agent-browser open'
- 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
- 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
- Do not inject other axe-core builds or freeze globals into agent-browser's __agent_browser_a11y_*__ isolated worlds
- Re-navigate before auditing when a session has been reused for many audits
- Keep agent-browser updated so the versioned world name changes with axe upgrades
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.