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
- Remove ClientOptions.Env and keep the Env on the connection (preferred for child-process transports).
- Alternatively remove the connection-level Env and keep only ClientOptions.Env.
- 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
- Adopt one convention: always put child-process env on the connection, never on client options.
- Search codebase for 'Env:' assignments to the client options when refactoring.
- Centralize client construction in a single helper so env placement is consistent.
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
- Env is not supported with InProcessConnection: the…
- WorkingDirectory is not supported with InProcessConnection…
- Telemetry is not supported with InProcessConnection…
- GitHubToken and UseLoggedInUser cannot be used with…
- err.Error()
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)