garrytan/gstack · error · Error
Ref ${selector} not found. Run 'snapshot' to get fresh refs.
Error message
Ref ${selector} not found. Run 'snapshot' to get fresh refs. What it means
Thrown by TabSession.resolveRef when a selector starting with `@e` or `@c` does not match any key in the current refMap. The refMap is populated by the most recent `snapshot` call and cleared on main-frame navigation; refs are short-lived identifiers, not persistent CSS selectors.
Source
Thrown at browse/src/tab-session.ts:93
// ─── Ref Map ──────────────────────────────────────────────
setRefMap(refs: Map<string, RefEntry>) {
this.refMap = refs;
}
clearRefs() {
this.refMap.clear();
}
/**
* Resolve a selector that may be a @ref (e.g., "@e3", "@c1") or a CSS selector.
* Returns { locator } for refs or { selector } for CSS selectors.
*/
async resolveRef(selector: string): Promise<{ locator: Locator } | { selector: string }> {
if (selector.startsWith('@e') || selector.startsWith('@c')) {
const ref = selector.slice(1); // "e3" or "c1"
const entry = this.refMap.get(ref);
if (!entry) {
throw new Error(
`Ref ${selector} not found. Run 'snapshot' to get fresh refs.`
);
}
const count = await entry.locator.count();
if (count === 0) {
throw new Error(
`Ref ${selector} (${entry.role} "${entry.name}") is stale — element no longer exists. ` +
`Run 'snapshot' for fresh refs.`
);
}
return { locator: entry.locator };
}
return { selector };
}
/** Get the ARIA role for a ref selector, or null for CSS selectors / unknown refs. */
getRefRole(selector: string): string | null {
if (selector.startsWith('@e') || selector.startsWith('@c')) {View on GitHub (pinned to 94993f7401)
Solutions
- Run `snapshot` again and re-read the ref list before retrying the action.
- Switch to a CSS selector if you need a stable target that survives navigation.
- Confirm you are operating on the same tab that produced the snapshot.
- For scripted flows, call snapshot immediately before each action rather than caching refs across navigations.
Example fix
// before await click(session, '@e5'); // refMap cleared by nav // after await snapshot(session); // refresh refs await click(session, '@e5');
Defensive patterns
Strategy: retry
Validate before calling
function refExists(session: TabSession, selector: string): boolean {
if (!selector.startsWith('@e') && !selector.startsWith('@c')) return true; // CSS — out of scope
const ref = selector.slice(1);
return session.getRefCount() > 0 && session.getRefEntries().some(e => e.ref === ref);
} Type guard
function isKnownRef(session: TabSession, sel: string): boolean {
if (!/^@[ec]/.test(sel)) return true;
return session.getRefEntries().some(e => e.ref === sel.slice(1));
} Try / catch
async function clickSafe(session: TabSession, sel: string) {
try { return await click(session, sel); }
catch (e: any) {
if (/^Ref .* not found/.test(e.message)) {
await snapshot(session); // refresh refs
return click(session, sel);
}
throw e;
}
} Prevention
- Always run snapshot before any @e/@c action in the same tab.
- Re-snapshot after every navigation, including SPA route changes.
- If you need a stable target, switch to a CSS selector with data-testid.
- Keep refs scoped per tab — they do not transfer across tabs.
When it happens
Trigger: Calling click/type/scroll/fill with a ref before running `snapshot`; calling it after a navigation that cleared the refMap; using a ref like `@e99` that was never issued; misspelling the ref (`@e1` vs `@E1` — case sensitive).
Common situations: A long agent session where the page auto-navigated between the snapshot and the action; copy-pasting a ref from an old snapshot output; switching tabs and forgetting refs are per-tab.
Related errors
- Ref ${selector} (${entry.role} "${entry.name}") is stale — e
- Invalid file URL: file:/// has no path. Use file:///<absolut
- Invalid file URL: ${url}. Use file:///<absolute-path> or fil
- Invalid file URL: file:// is empty. Use file:///<absolute-pa
- Invalid URL: ${url}
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/c91ed11d42f4b3ac.
Report an issue: GitHub.