Skip to content

Commit 66628ea

Browse files
Fix Go stderr capture: wait for reader goroutine before accessing buffer
Use StderrPipe with explicit ReadAll in goroutine, wait for EOF signal before accessing buffer. This ensures all stderr data is captured even when the process exits quickly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent dccd8a7 commit 66628ea

1 file changed

Lines changed: 25 additions & 6 deletions

File tree

go/client.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"encoding/json"
3535
"errors"
3636
"fmt"
37+
"io"
3738
"net"
3839
"os"
3940
"os/exec"
@@ -48,6 +49,11 @@ import (
4849
"github.com/github/copilot-sdk/go/rpc"
4950
)
5051

52+
// readAll reads all data from r until EOF or error, returning the data read.
53+
func readAll(r io.Reader) ([]byte, error) {
54+
return io.ReadAll(r)
55+
}
56+
5157
// Client manages the connection to the Copilot CLI server and provides session management.
5258
//
5359
// The Client can either spawn a CLI server process or connect to an existing server.
@@ -1092,22 +1098,35 @@ func (c *Client) startCLIServer(ctx context.Context) error {
10921098
return fmt.Errorf("failed to create stdout pipe: %w", err)
10931099
}
10941100

1095-
// Capture stderr directly to buffer (not via pipe, which gets closed on Wait())
1096-
c.process.Stderr = &c.stderrBuf
1101+
stderr, err := c.process.StderrPipe()
1102+
if err != nil {
1103+
return fmt.Errorf("failed to create stderr pipe: %w", err)
1104+
}
10971105

10981106
if err := c.process.Start(); err != nil {
10991107
return fmt.Errorf("failed to start CLI server: %w", err)
11001108
}
11011109

1110+
// Read stderr in background - reads until EOF (process exit) then signals completion
1111+
stderrDone := make(chan struct{})
1112+
go func() {
1113+
// ReadAll reads until EOF, which happens when process terminates
1114+
data, _ := readAll(stderr)
1115+
c.stderrBuf.Write(data)
1116+
close(stderrDone)
1117+
}()
1118+
11021119
// Monitor process exit to signal pending requests
11031120
c.processDone = make(chan struct{})
11041121
go func() {
1105-
err := c.process.Wait()
1122+
waitErr := c.process.Wait()
1123+
// Wait for stderr reader to finish before accessing buffer
1124+
<-stderrDone
11061125
stderrOutput := strings.TrimSpace(c.stderrBuf.String())
11071126
if stderrOutput != "" {
1108-
c.processError = fmt.Errorf("CLI process exited: %v\nstderr: %s", err, stderrOutput)
1109-
} else if err != nil {
1110-
c.processError = fmt.Errorf("CLI process exited: %v", err)
1127+
c.processError = fmt.Errorf("CLI process exited: %v\nstderr: %s", waitErr, stderrOutput)
1128+
} else if waitErr != nil {
1129+
c.processError = fmt.Errorf("CLI process exited: %v", waitErr)
11111130
} else {
11121131
c.processError = fmt.Errorf("CLI process exited unexpectedly")
11131132
}

0 commit comments

Comments
 (0)