chenhg5/cc-connect · warning

xdg-open not found (headless server?): %w

Error message

xdg-open not found (headless server?): %w

What it means

openBrowser, used by `cc-connect web` (runWeb), opens the dashboard URL in a browser. On Linux it first looks up xdg-open via exec.LookPath; when missing it returns "xdg-open not found (headless server?)" instead of silently failing to start a browser. The web server itself still runs — the caller is expected to print the URL so the user can open it remotely.

Source

Thrown at cmd/cc-connect/web.go:92

		fmt.Printf("\nCould not open browser automatically.\n")
		fmt.Printf("Open this URL in your browser:\n")
		fmt.Printf("  %s/login?token=%s\n", baseURL, token)
		fmt.Printf("\nNote: make sure cc-connect is running (it hosts the web admin on port %d).\n", port)
	}
}

func openBrowser(rawURL string) error {
	switch runtime.GOOS {
	case "darwin":
		return exec.Command("open", rawURL).Start()
	case "linux":
		if isWSL() {
			return exec.Command("cmd.exe", "/c", "start", rawURL).Start()
		}
		// On headless Linux, xdg-open is often unavailable.
		// Check early and return a clear error so the caller can print the URL.
		if _, err := exec.LookPath("xdg-open"); err != nil {
			return fmt.Errorf("xdg-open not found (headless server?): %w", err)
		}
		return exec.Command("xdg-open", rawURL).Start()
	case "windows":
		return exec.Command("cmd", "/c", "start", rawURL).Start()
	default:
		return fmt.Errorf("unsupported OS: %s", runtime.GOOS)
	}
}

func isWSL() bool {
	data, err := os.ReadFile("/proc/version")
	if err != nil {
		return false
	}
	return strings.Contains(strings.ToLower(string(data)), "microsoft")
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Open the printed URL manually in your local browser — the web server is running regardless.
  2. Install xdg-utils (apt install xdg-utils / dnf install xdg-utils) if the machine actually has a desktop.
  3. Set $BROWSER to a usable browser or script so the opener has a target.
  4. If connecting remotely, use SSH port forwarding: ssh -L 8080:localhost:8080 user@host, then browse http://localhost:8080.
  5. On WSL, verify /proc/version is readable so isWSL() can route to cmd.exe /c start instead of xdg-open.

Example fix

// before
cc-connect web   # on headless server: xdg-open not found
// after
cc-connect web
# copy the printed URL, or: ssh -L 8080:localhost:8080 user@server
Defensive patterns

Strategy: fallback

Validate before calling

if _, err := exec.LookPath("xdg-open"); err != nil {
    fmt.Println("No browser available; open the URL manually:", url)
}

Try / catch

if err := openBrowser(url); err != nil {
    var notFoundErr *exec.Error
    if errors.As(err, &notFoundErr) || strings.Contains(err.Error(), "xdg-open not found") {
        fmt.Println("Web UI running at:", url) // print URL instead of failing
    }
}

Prevention

When it happens

Trigger: Running `cc-connect web` (which calls openBrowser) on a Linux machine without xdg-open in PATH — typically headless servers, containers, SSH sessions without a desktop environment, or minimal distro images lacking xdg-utils.

Common situations: Starting the web dashboard on a VPS or Docker container; SSH-ing into a CI box; a slim Debian/Alpine image without the xdg-utils package.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/67c36a1600a78d5e. Report an issue: GitHub.