{"record":{"id":"2fb485e95e851fe4","repo":"cli/cli","slug":"failed-to-open-jupyterlab-in-browser-w","errorCode":null,"errorMessage":"failed to open JupyterLab in browser: %w","messagePattern":"failed to open JupyterLab in browser: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/cmd/codespace/jupyter.go","lineNumber":94,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\tdefer listen.Close()\n\tdestPort := listen.Addr().(*net.TCPAddr).Port\n\n\ttunnelClosed := make(chan error, 1)\n\tgo func() {\n\t\topts := portforwarder.ForwardPortOpts{\n\t\t\tPort: serverPort,\n\t\t}\n\t\ttunnelClosed <- fwd.ForwardPortToListener(ctx, opts, listen)\n\t}()\n\n\t// Server URL contains an authentication token that must be preserved\n\ttargetUrl := strings.Replace(serverUrl, fmt.Sprintf(\"%d\", serverPort), fmt.Sprintf(\"%d\", destPort), 1)\n\terr = a.browser.Browse(targetUrl)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open JupyterLab in browser: %w\", err)\n\t}\n\n\tfmt.Fprintln(a.io.Out, targetUrl)\n\n\tselect {\n\tcase err := <-tunnelClosed:\n\t\treturn fmt.Errorf(\"tunnel closed: %w\", err)\n\tcase <-ctx.Done():\n\t\treturn nil // success\n\t}\n}\n","sourceCodeStart":76,"sourceCodeEnd":106,"githubUrl":"https://github.com/cli/cli/blob/0eeec0b92edbe70199f9768522f831d3534f41ad/pkg/cmd/codespace/jupyter.go#L76-L106","documentation":"Raised by gh codespace jupyter when a.browser.Browse(targetUrl) fails to open the constructed JupyterLab URL in the user's browser. The targetUrl is derived from the server-reported URL with the server port replaced by the local forwarded port, and it embeds Jupyter's authentication token, which must survive the rewrite. The failure is purely local: no valid browser/launcher could be spawned for this URL.","triggerScenarios":"Running `gh codespace jupyter` in an environment where no browser can be opened: headless server or container with no display, SSH session without X forwarding, BROWSER env var pointing at a nonexistent executable, or an OS where the xdg-open/open mechanism fails.","commonSituations":"Running gh over SSH to a remote box (no GUI); CI containers; WSL without a Windows browser bridge configured; BROWSER set to a broken script; unusual desktop environments lacking xdg-open.","solutions":["If on a headless/remote machine, set BROWSER to a no-op or run with a mechanism that prints the URL instead (e.g. use `gh codespace jupyter` locally, or copy the printed URL manually after starting)","Check the BROWSER environment variable: unset it or point it to a real executable (`echo $BROWSER`)","On Linux ensure xdg-open exists (install xdg-utils); on WSL ensure wslview/browser integration is set up","Inspect targetUrl in the error chain for oddities - a malformed serverUrl (e.g. port number appearing in host) can break the string port replacement"],"exampleFix":"// before: headless host fails to spawn a browser\nexport BROWSER=/nonexistent\ngh codespace jupyter\n// after: print-only on headless hosts, open locally by hand\nexport BROWSER=echo   # gh prints the URL (token included) instead of failing","handlingStrategy":"fallback","validationCode":"// Check a browser is actually spawnable before starting the tunnel work\nif browser := os.Getenv(\"BROWSER\"); browser != \"\" {\n    if _, err := exec.LookPath(strings.Fields(browser)[0]); err != nil { /* will fail; set BROWSER=echo */ }\n}","typeGuard":null,"tryCatchPattern":"// Fallback: if Browse fails, degrade to printing the URL (token included) instead of erroring\nif err := a.browser.Browse(targetUrl); err != nil {\n    fmt.Fprintf(a.io.ErrOut, \"could not open browser: %v\\n\", err)\n    fmt.Fprintln(a.io.Out, targetUrl) // user opens manually\n}","preventionTips":["On headless hosts set BROWSER=echo so gh prints the URL","Keep xdg-utils installed on Linux desktops","In SSH sessions, prefer running gh locally or copy the printed URL","Never strip the token from the URL when copying it manually"],"tags":["go","gh-cli","codespaces","jupyter","browser","local-env"],"backgroundTag":null,"analyzedSha":"0eeec0b92edbe70199f9768522f831d3534f41ad","analyzedAt":"2026-08-15T12:31:05.478Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}