{"record":{"id":"410dd5bf9af3929f","repo":"openclaw/openclaw","slug":"ambiguous-browser-tab-reference-410dd5","errorCode":null,"errorMessage":"ambiguous browser tab reference","messagePattern":"ambiguous browser tab reference","errorType":"exception","errorClass":"BrowserTargetAmbiguousError","httpStatus":409,"severity":"error","filePath":"extensions/browser/src/browser/server-context.selection.ts","lineNumber":217,"sourceCode":"      const last = stickyTargetId ?? \"\";\n      const lastResolved = last ? resolveById(last, { exactTargetId: true }) : null;\n      if (lastResolved && lastResolved !== \"AMBIGUOUS\") {\n        return lastResolved;\n      }\n      // Sticky selection is an identity promise. If it disappears without a proven\n      // alias migration, require a fresh explicit choice instead of guessing a tab.\n      if (last) {\n        return null;\n      }\n      // Prefer a real page tab first (avoid service workers/background targets).\n      const page = candidates.find((t) => (t.type ?? \"page\") === \"page\");\n      return page ?? candidates.at(0) ?? null;\n    };\n\n    const chosen = targetId ? resolveById(targetId) : pickDefault();\n\n    if (chosen === \"AMBIGUOUS\") {\n      throw new BrowserTargetAmbiguousError();\n    }\n    if (!chosen) {\n      throw new BrowserTabNotFoundError({ input: targetId ?? stickyTargetId });\n    }\n    runtime.lastTargetId = chosen.targetId;\n    return chosen;\n  };\n\n  const resolveTargetIdOrThrow = async (\n    targetId: string,\n    options?: BrowserTabTargetOptions,\n  ): Promise<string> => {\n    const tabs = await listTabs(options);\n    if (options?.exactTargetId) {\n      const exactTarget = tabs.find((tab) => tab.targetId === targetId);\n      if (!exactTarget) {\n        throw new BrowserTabNotFoundError({ input: targetId });\n      }","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/openclaw/openclaw/blob/01804a75319da4b69c9ab98ceaa30477e22b8c0b/extensions/browser/src/browser/server-context.selection.ts#L199-L235","documentation":"Thrown by BrowserTargetAmbiguousError (HTTP 409) in the listTabs default-selection path when resolveById returns the sentinel 'AMBIGUOUS'. This happens when a non-exact tab reference (label or alias) matches more than one candidate tab. The default error message is 'ambiguous browser tab reference'.","triggerScenarios":"listTabs() called with a targetId that resolves via resolveTargetIdFromTabs which returns { ok:false, reason:'ambiguous' }, and no exactTargetId override was supplied.","commonSituations":"Two tabs share the same label/alias; a shorthand matches multiple tab titles; positional/tabId alias collides after tabs were duplicated; the user opened two identical pages.","solutions":["Use action=tabs to list current tabs and pass a more specific suggestedTargetId, tabId, label, or raw targetId.","Pass exactTargetId:true to bypass alias resolution and match the raw targetId directly.","Close duplicate tabs so the alias is unique.","Use a raw targetId (longest, most specific identifier) instead of a short label."],"exampleFix":"// before: ambiguous label\nawait browser.tab('chat');  // matches 3 tabs\n// after: disambiguate with raw targetId from action=tabs\nawait browser.tab('A1B2C3D4...targetId', { exactTargetId: true });","handlingStrategy":"validation","validationCode":"// Resolve the tab id against current tabs before acting; detect ambiguity upfront\nasync function resolveUniquely(targetId: string, tabs): Promise<string | null> {\n  const matches = tabs.filter(t => t.label === targetId || t.alias === targetId);\n  if (matches.length > 1) return null; // ambiguous\n  return matches[0]?.targetId ?? null;\n}","typeGuard":"import { BrowserTargetAmbiguousError } from './errors';\nfunction isTabAmbiguous(err: unknown): err is BrowserTargetAmbiguousError {\n  return err instanceof BrowserTargetAmbiguousError;\n}","tryCatchPattern":"try {\n  await browser.tab(targetId);\n} catch (err) {\n  if (err instanceof BrowserTargetAmbiguousError) {\n    const tabs = await browser.tabs();\n    // ask the user / caller to disambiguate using a raw targetId\n    throw new Error(`ambiguous; candidates: ${tabs.map(t => t.targetId).join(', ')}`, { cause: err });\n  }\n  throw err;\n}","preventionTips":["Always use a raw targetId (exactTargetId:true) when you have one.","Call action=tabs first and pick a unique identifier.","Close duplicate tabs so aliases don't collide.","Avoid short shorthands that match multiple titles."],"tags":["browser","tabs","ambiguous","selection","resolution"],"backgroundTag":null,"analyzedSha":"01804a75319da4b69c9ab98ceaa30477e22b8c0b","analyzedAt":"2026-08-12T04:37:58.197Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}