github/copilot-sdk · error

failed to create stdout pipe

Error message

failed to create stdout pipe: %w

What it means

In stdio mode Connect wraps the error from process.StdoutPipe() with this message. The stdout pipe is required to read JSON-RPC responses from the CLI subprocess. Failure has the same root causes as the stdin pipe failure: wrong process state or OS resource limits.

Solutions

  1. Create a fresh Client per connection; never call Connect() after the process has started.
  2. Raise the file descriptor limit if fd exhaustion is reported in the wrapped error.
  3. Inspect the wrapped error for the precise OS reason.
  4. Verify no other code path invoked process.Start() before Connect().

Example fix

// before
process, _ := buildCmd()
process.Start()
client.Connect(ctx) // StdoutPipe fails: process already started

// after
client, _ := NewClient(WithCommand(buildCmd()))
if err := client.Connect(ctx); err != nil {
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

// create pipes only on a fresh, unstarted process; guard with state flag
if clientStarted { return errors.New("process already started; recreate client") }

Try / catch

if err := client.Connect(ctx); err != nil {
    if strings.Contains(err.Error(), "failed to create stdout pipe") {
        client, err = sdk.NewClient(opts)
        if err == nil { err = client.Connect(ctx) }
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Connect() with useStdio enabled when StdoutPipe() fails — process already started (pipes can only be created before Start), or fd/resource exhaustion.

Common situations: Double Connect() on one Client, running in containers with tight fd limits, or a custom wrapper that started the process before calling Connect.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/e8f2fce121159dad. Report an issue: GitHub.

Appendix: source

Thrown at go/client.go:2176

		if t.CaptureContent != nil {
			val := "false"
			if *t.CaptureContent {
				val = "true"
			}
			c.process.Env = setEnvValue(c.process.Env, "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", val)
		}
	}

	if c.useStdio {
		// For stdio mode, we need stdin/stdout pipes
		stdin, err := c.process.StdinPipe()
		if err != nil {
			return fmt.Errorf("failed to create stdin pipe: %w", err)
		}

		stdout, err := c.process.StdoutPipe()
		if err != nil {
			return fmt.Errorf("failed to create stdout pipe: %w", err)
		}

		c.process.Stderr = truncbuffer.NewTruncBuffer(stderrBufferSize)

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

		c.monitorProcess()

		// Create JSON-RPC client immediately
		c.client = jsonrpc2.NewClient(stdin, stdout)
		c.client.SetProcessDone(c.processDone, c.processErrorPtr)
		c.client.SetOnClose(c.handleConnectionClose)
		c.RPC = rpc.NewServerRPC(c.client)
		c.internalRPC = rpc.NewInternalServerRPC(c.client)
		c.setupNotificationHandler()
		c.client.Start()

View on GitHub (pinned to cd8cf15dc3)