Skip to content

gh cs ssh can hide errors on port forwarding #11206

@williammartin

Description

@williammartin

Describe the bug

When running gh cs ssh we forward the codespaces internal port (16634) in order to connect via GRPC.

// Tunnel the remote gRPC server port to the local port
go func() {
// Start forwarding the port locally
opts := portforwarder.ForwardPortOpts{
Port: codespacesInternalPort,
Internal: true,
}
ch <- fwd.ForwardPortToListener(pfctx, opts, listener)
}()
var conn *grpc.ClientConn
go func() {
// Attempt to connect to the port
opts := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
}
conn, err = grpc.NewClient(localAddress, opts...)
ch <- err // nil if we successfully connected
}()
// Wait for the connection to be established or for the context to be cancelled
select {
case <-ctx.Done():
return nil, ctx.Err()
case err := <-ch:
if err != nil {
return nil, err
}
}

Both the port forward and the grpc client construction write to ch when completing, then the select unblocks. However, we've seen port forwarding fail but grpc client construction succeed, resulting in a failure much down the line that was not surfaced.

Affected version

➜  gh version
gh version 2.74.2 (2025-06-17)
https://github.com/cli/cli/releases/tag/v2.74.2

Steps to reproduce the behavior

Not possible to produce without a port forwarding issue or code changes.

Apply the following patch and see that the error doesn't bubble up:

diff --git a/internal/codespaces/rpc/invoker.go b/internal/codespaces/rpc/invoker.go
index 6ba8843ac..d1d19e5b9 100644
--- a/internal/codespaces/rpc/invoker.go
+++ b/internal/codespaces/rpc/invoker.go
@@ -5,6 +5,7 @@ package rpc
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"net"
 	"os"
@@ -104,12 +105,8 @@ func connect(ctx context.Context, fwd portforwarder.PortForwarder) (Invoker, err
 
 	// Tunnel the remote gRPC server port to the local port
 	go func() {
-		// Start forwarding the port locally
-		opts := portforwarder.ForwardPortOpts{
-			Port:     codespacesInternalPort,
-			Internal: true,
-		}
-		ch <- fwd.ForwardPortToListener(pfctx, opts, listener)
+		time.Sleep(time.Second)
+		ch <- errors.New("omg")
 	}()
 
 	var conn *grpc.ClientConn

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcodespacesgh-codespacerelating to the gh codespace commandpriority-3Affects a small number of users or is largely cosmetic

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions