cli/cli · warning

failed to open JupyterLab in browser: %w

Error message

failed to open JupyterLab in browser: %w

What it means

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.

Source

Thrown at pkg/cmd/codespace/jupyter.go:94

	if err != nil {
		return err
	}
	defer listen.Close()
	destPort := listen.Addr().(*net.TCPAddr).Port

	tunnelClosed := make(chan error, 1)
	go func() {
		opts := portforwarder.ForwardPortOpts{
			Port: serverPort,
		}
		tunnelClosed <- fwd.ForwardPortToListener(ctx, opts, listen)
	}()

	// Server URL contains an authentication token that must be preserved
	targetUrl := strings.Replace(serverUrl, fmt.Sprintf("%d", serverPort), fmt.Sprintf("%d", destPort), 1)
	err = a.browser.Browse(targetUrl)
	if err != nil {
		return fmt.Errorf("failed to open JupyterLab in browser: %w", err)
	}

	fmt.Fprintln(a.io.Out, targetUrl)

	select {
	case err := <-tunnelClosed:
		return fmt.Errorf("tunnel closed: %w", err)
	case <-ctx.Done():
		return nil // success
	}
}

View on GitHub (pinned to 0eeec0b92e)

Solutions

  1. 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)
  2. Check the BROWSER environment variable: unset it or point it to a real executable (`echo $BROWSER`)
  3. On Linux ensure xdg-open exists (install xdg-utils); on WSL ensure wslview/browser integration is set up
  4. Inspect targetUrl in the error chain for oddities - a malformed serverUrl (e.g. port number appearing in host) can break the string port replacement

Example fix

// before: headless host fails to spawn a browser
export BROWSER=/nonexistent
gh codespace jupyter
// after: print-only on headless hosts, open locally by hand
export BROWSER=echo   # gh prints the URL (token included) instead of failing
Defensive patterns

Strategy: fallback

Validate before calling

// Check a browser is actually spawnable before starting the tunnel work
if browser := os.Getenv("BROWSER"); browser != "" {
    if _, err := exec.LookPath(strings.Fields(browser)[0]); err != nil { /* will fail; set BROWSER=echo */ }
}

Try / catch

// Fallback: if Browse fails, degrade to printing the URL (token included) instead of erroring
if err := a.browser.Browse(targetUrl); err != nil {
    fmt.Fprintf(a.io.ErrOut, "could not open browser: %v\n", err)
    fmt.Fprintln(a.io.Out, targetUrl) // user opens manually
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of cli/cli@0eeec0b92e (2026-08-15). Data as JSON: /api/errors/2fb485e95e851fe4. Report an issue: GitHub.