cli/cli · error

tunnel closed: %w

Error message

tunnel closed: %w

What it means

Reported by gh codespace jupyter when the port-forward tunnel to the codespace terminates with an error. The tunnel goroutine runs fwd.ForwardPortToListener(ctx, opts, listen) and sends its result on tunnelClosed; receiving an error there means the websocket tunnel relaying the local listener to the codespace port died or failed to start forwarding. It is distinct from ctx.Done() on the same select, which is the normal shutdown path.

Source

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

	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. Simply rerun `gh codespace jupyter` - transient tunnel drops are the most common cause
  2. Check the codespace is still running: `gh codespace list` (state should be Running, not Shutdown/Deleted)
  3. If drops are frequent on your network, keep the session active or adjust idle timeout: `gh codespace edit -m ... --idle-timeout` / org policy
  4. Read the wrapped %w error: 'context canceled' during intentional shutdown is benign; websocket 1006/close errors point to network or relay issues
Defensive patterns

Strategy: try-catch

Try / catch

// Distinguish intentional shutdown from real tunnel failure
select {
case err := <-tunnelClosed:
    if errors.Is(err, context.Canceled) { return nil } // normal shutdown
    return fmt.Errorf("tunnel closed: %w", err)
case <-ctx.Done():
    return nil
}

Prevention

When it happens

Trigger: `gh codespace jupyter` running with the browser open, then the tunnel drops: codespace stopped or deleted mid-session, network interruption killing the websocket, relay server closing the connection, or ForwardPortToListener failing immediately (e.g. the codespace port not listening yet inside the container).

Common situations: Laptop sleep/resume or Wi-Fi change dropping the tunnel; the codespace auto-stopping after idle timeout; Jupyter server process dying inside the codespace; flaky corporate networks that idle-out long-lived websockets.

Related errors


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