{"record":{"id":"e81191ce3e9f8a1f","repo":"sgl-project/sglang","slug":"browser-open-requires-a-cursor-or-url","errorCode":null,"errorMessage":"browser.open requires a cursor or url","messagePattern":"browser\\.open requires a cursor or url","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/entrypoints/tool.py","lineNumber":196,"sourceCode":"                or self._best_snippet(page)\n                or page.get(\"summary\")\n                or \"\"\n            )\n            return self._format_matches(pattern, text)\n\n        searchable_text = \"\\n\\n\".join(\n            self._best_snippet(page) for page in state[\"pages\"].values()\n        )\n        return self._format_matches(pattern, searchable_text)\n\n    def _resolve_url(self, context: \"ConversationContext\", args: dict[str, Any]) -> str:\n        if args.get(\"url\"):\n            return str(args[\"url\"])\n\n        state = self._browser_state(context)\n        cursor = self._normalize_cursor(args.get(\"cursor\"))\n        if not cursor:\n            raise ValueError(\"browser.open requires a cursor or url\")\n\n        page = state[\"pages\"].get(cursor)\n        if page is None:\n            raise ValueError(f\"Unknown browser cursor: {cursor}\")\n\n        url = page.get(\"url\") or page.get(\"id\")\n        if not url:\n            raise ValueError(f\"No URL recorded for browser cursor: {cursor}\")\n        return str(url)\n\n    def _normalize_cursor(self, cursor: Any) -> str | None:\n        if cursor is None:\n            return None\n        cursor_str = str(cursor)\n        # GPT-OSS emits 0 as a 1-based cursor; map it to the first result.\n        if cursor_str == \"0\":\n            return \"1\"\n        return cursor_str","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/entrypoints/tool.py#L178-L214","documentation":"SGLang's browser URL resolver cannot determine what to open: browser.open arguments contain neither a 'url' nor a usable 'cursor' referring to a previously seen search result. One of the two is mandatory.","triggerScenarios":"Calling browser.open with args like {} or {\"cursor\": null/\"\"}, after _normalize_cursor returns None and no url key exists.","commonSituations":"Model emitting browser.open with no arguments, or passing a cursor field under a different name; also first browser.open in a session with no prior browser.search to establish cursors.","solutions":["Pass an explicit \"url\" in browser.open arguments","Or first run browser.search, then reference a result via its \"cursor\" value","Validate that args contains url or a non-empty cursor before dispatching"],"exampleFix":"# before\nargs = {}\n# after\nargs = {\"url\": \"https://sglang.ai\"}","handlingStrategy":"validation","validationCode":"if not args.get(\"url\") and not args.get(\"cursor\"):\n    raise ValueError(\"browser.open requires a cursor or url\")","typeGuard":"def resolvable_open_args(args: dict) -> bool:\n    return bool(args.get(\"url\") or args.get(\"cursor\"))","tryCatchPattern":null,"preventionTips":["Always prefer explicit url in browser.open when known","Ensure a browser.search precedes any cursor-based browser.open"],"tags":["browser","open","argument-validation","tool-calling"],"backgroundTag":"missing-required-tool-argument","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}