github/copilot-sdk · error

failed to create stdin pipe

Error message

failed to create stdin pipe: %w

What it means

In stdio mode Connect wraps the error from process.StdinPipe() with this message. The SDK could not obtain the stdin pipe to the CLI subprocess, so JSON-RPC communication cannot be set up. This typically reflects OS-level resource or process-state problems.

Solutions

  1. Do not reuse a Client whose process already started; create a new Client and Connect once.
  2. Check/increase the file descriptor limit (ulimit -n) in the runtime environment.
  3. Log the wrapped underlying error (%w) to identify the exact OS failure.
  4. Ensure the command path is correct so the exec.Cmd is in a valid pre-start state.

Example fix

// before
client.Connect(ctx) // first connect
client.Connect(ctx) // second connect -> StdinPipe error

// after
if !client.IsConnected() {
    if err := client.Connect(ctx); err != nil {
        return fmt.Errorf("connect: %w", err)
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure fd headroom before connecting
var lim syscall.Rlimit
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
if lim.Cur < 256 { lim.Cur = 256; syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim) }

Try / catch

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

Prevention

When it happens

Trigger: Calling Connect() with useStdio enabled when os/exec cannot create the StdinPipe — e.g. process already started (StdinPipe called twice), or fd exhaustion.

Common situations: Calling Connect() twice on the same Client, running under a environment with a low file-descriptor limit (ulimit -n), or embedding the SDK in a sandboxed runtime that restricts pipes.

Related errors


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

Appendix: source

Thrown at go/client.go:2171

			c.process.Env = setEnvValue(c.process.Env, "COPILOT_OTEL_EXPORTER_TYPE", t.ExporterType)
		}
		if t.SourceName != "" {
			c.process.Env = setEnvValue(c.process.Env, "COPILOT_OTEL_SOURCE_NAME", t.SourceName)
		}
		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)

View on GitHub (pinned to cd8cf15dc3)