github/copilot-sdk · error

Set environment variables via either the client-level Env…

Error message

Set environment variables via either the client-level Env option or the connection's Env, not both. Prefer the connection-level Env for child-process transports.

What it means

NewClient panics when environment variables are supplied twice for a child-process transport: both the client-level ClientOptions.Env and the connection's own Env (e.g. StdioConnection.Env) are non-nil. The library rejects the ambiguity so it is always clear which environment block the child receives; the connection-level Env is the preferred location.

Solutions

  1. Remove ClientOptions.Env and keep the Env on the connection (preferred for child-process transports).
  2. Alternatively remove the connection-level Env and keep only ClientOptions.Env.
  3. Note: an explicit empty connection env (non-nil, empty map) is authoritative, so nil-ing the client-level Env is enough.

Example fix

// before
client := clientpkg.NewClient(&clientpkg.Options{
    Env: map[string]string{"FOO": "bar"},
    Connection: clientpkg.StdioConnection{
        Env: map[string]string{"FOO": "bar"},
    },
})
// after
client := clientpkg.NewClient(&clientpkg.Options{
    Connection: clientpkg.StdioConnection{
        Env: map[string]string{"FOO": "bar"},
    },
})
Defensive patterns

Strategy: validation

Validate before calling

if opts.Env != nil {
    if cp, ok := opts.Connection.(clientpkg.StdioConnection); ok && cp.Env != nil {
        return fmt.Errorf("Env set on both client options and connection")
    }
}

Prevention

When it happens

Trigger: Calling NewClient with a childProcessConnection (e.g. StdioConnection{Env: ...}) whose connEnv() is non-nil while ClientOptions.Env is also non-nil. Panics in validateEnvironmentOptions (go/client.go:124).

Common situations: Refactoring that moves Env from client options onto the connection (or vice versa) leaves the old setting in place; copying example code that sets Env in both places.

Related errors


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

Appendix: source

Thrown at go/client.go:124

// transports each own their OS process, so per-connection env is allowed, but
// setting it in both the client-level option and the connection is rejected.
func validateEnvironmentOptions(connection RuntimeConnection, opts *ClientOptions) {
	if _, ok := connection.(InProcessConnection); ok {
		if opts.Env != nil {
			panic("Env is not supported with InProcessConnection: the in-process transport loads the native runtime into the shared host process, whose single environment block cannot carry per-client values. Set the variables on the host process environment instead.")
		}
		if opts.WorkingDirectory != "" {
			panic("WorkingDirectory is not supported with InProcessConnection: the native runtime shares the host process working directory. Use a child-process transport, or set the process working directory before creating the client.")
		}
		if opts.Telemetry != nil {
			panic("Telemetry is not supported with InProcessConnection: telemetry configuration is lowered to environment variables read by native runtime code running in the shared host process, so per-client telemetry cannot be honored in-process. Configure telemetry via the host process environment, or use a child-process transport.")
		}
		return
	}

	if cp, ok := connection.(childProcessConnection); ok {
		if cp.connEnv() != nil && opts.Env != nil {
			panic("Set environment variables via either the client-level Env option or the connection's Env, not both. Prefer the connection-level Env for child-process transports.")
		}
	}
}

// Client manages the connection to the Copilot CLI server and provides session management.
//
// The Client can either spawn a CLI server process or connect to an existing server.
// It handles JSON-RPC communication, session lifecycle, tool execution, and permission requests.
//
// Example:
//
//	// Create a client with default options (spawns CLI server using stdio)
//	client := copilot.NewClient(nil)
//
//	// Or connect to an existing server
//	client := copilot.NewClient(&copilot.ClientOptions{
//	    Connection: copilot.URIConnection{URL: "localhost:3000"},
//	})

View on GitHub (pinned to cd8cf15dc3)