sgl-project/sglang · error · ValueError
Unknown browser cursor: {cursor}
Error message
Unknown browser cursor: {cursor} What it means
SGLang's browser resolver was given a cursor (e.g. a search-result index like '0' or '1-3'), but the per-context browser state dict has no page entry for it. Cursors only exist after a search/open result recorded them on the conversation context.
Source
Thrown at python/sglang/srt/entrypoints/tool.py:200
return self._format_matches(pattern, text)
searchable_text = "\n\n".join(
self._best_snippet(page) for page in state["pages"].values()
)
return self._format_matches(pattern, searchable_text)
def _resolve_url(self, context: "ConversationContext", args: dict[str, Any]) -> str:
if args.get("url"):
return str(args["url"])
state = self._browser_state(context)
cursor = self._normalize_cursor(args.get("cursor"))
if not cursor:
raise ValueError("browser.open requires a cursor or url")
page = state["pages"].get(cursor)
if page is None:
raise ValueError(f"Unknown browser cursor: {cursor}")
url = page.get("url") or page.get("id")
if not url:
raise ValueError(f"No URL recorded for browser cursor: {cursor}")
return str(url)
def _normalize_cursor(self, cursor: Any) -> str | None:
if cursor is None:
return None
cursor_str = str(cursor)
# GPT-OSS emits 0 as a 1-based cursor; map it to the first result.
if cursor_str == "0":
return "1"
return cursor_str
def _cursor_for_url(self, state: dict[str, Any], url: str) -> str | None:
for cursor, page in state["pages"].items():
if page.get("url") == url or page.get("id") == url:View on GitHub (pinned to 0132848349)
Solutions
- Re-run browser.search to (re)populate cursors before referencing them
- Use a cursor that actually appears in the last formatted search results
- Persist/reattach the browser state on the context across turns if needed
Example fix
# before
args = {"cursor": "7"} # only 3 results existed
# after
args = {"cursor": "2"} Defensive patterns
Strategy: validation
Validate before calling
state = getattr(context, "_sglang_exa_browser_state", {"pages": {}})
cursor = str(args["cursor"])
assert cursor in state["pages"], f"unknown cursor {cursor}" Type guard
def cursor_exists(context, cursor: str) -> bool:
pages = getattr(context, "_sglang_exa_browser_state", {}).get("pages", {})
return str(cursor) in pages Try / catch
try:
url = tool._resolve_url(context, args)
except ValueError as e:
if "Unknown browser cursor" in str(e):
await tool._dispatch_browser_call(context, "browser.search", {"query": ...})
url = tool._resolve_url(context, args) Prevention
- Restrict cursors to those present in the last search result formatting
- Carry browser state on the same context object across turns
When it happens
Trigger: browser.open or browser.find with {"cursor": "5"} when only cursors 0-2 were recorded in context._sglang_exa_browser_state['pages'], or a cursor from a different/earlier conversation context.
Common situations: Model hallucinating a cursor beyond the result list, reusing a cursor after context state was reset, or a new context object lacking prior state (e.g. multi-turn session where _sglang_exa_browser_state was not persisted).
Related errors
- No browser tool call found
- browser.search requires a query
- browser.find requires a pattern
- Unknown browser action: {recipient}
- browser.open requires a cursor or url
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/718dac5f44bbb736.
Report an issue: GitHub.