docker/compose · critical
restoring env for %q: %w
Error message
restoring env for %q: %w
What it means
To keep public OTEL init functions from reading OS env vars, traceClientFromDockerContext temporarily unsets all OTEL_* variables and restores them afterwards in a defer. If os.Setenv fails while restoring a key, the function panics with 'restoring env for %q: %w'. This is a hard crash, not a returned error, and it can leave the env half-restored.
Source
Thrown at internal/tracing/docker_context.go:55
func traceClientFromDockerContext(dockerCli command.Cli, otelEnv envMap) (otlptrace.Client, error) {
// attempt to extract an OTEL config from the Docker context to enable
// automatic integration with Docker Desktop;
cfg, err := ConfigFromDockerContext(dockerCli.ContextStore(), dockerCli.CurrentContext())
if err != nil {
return nil, fmt.Errorf("loading otel config from docker context metadata: %w", err)
}
if cfg.Endpoint == "" {
return nil, nil
}
// HACK: unfortunately _all_ public OTEL initialization functions
// implicitly read from the OS env, so temporarily unset them all and
// restore afterwards
defer func() {
for k, v := range otelEnv {
if err := os.Setenv(k, v); err != nil {
panic(fmt.Errorf("restoring env for %q: %w", k, err))
}
}
}()
for k := range otelEnv {
if err := os.Unsetenv(k); err != nil {
return nil, fmt.Errorf("stashing env for %q: %w", k, err)
}
}
conn, err := grpc.NewClient(cfg.Endpoint,
grpc.WithContextDialer(memnet.DialEndpoint),
// this dial is restricted to using a local Unix socket / named pipe,
// so there is no need for TLS
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
return nil, fmt.Errorf("initializing otel connection from docker context metadata: %w", err)
}View on GitHub (pinned to ddc4b044b6)
Solutions
- Check the wrapped %w (typically OOM-related) and address memory pressure / raise limits for the compose process
- Eliminate concurrent env mutation: do not run code that sets OTEL_* vars in parallel with startup
- If seen in tests, serialize env-touching tests or unset OTEL_* before invoking compose APIs
- Upgrade compose — a panic here is a last-resort guard, and newer builds may harden this path
Defensive patterns
Strategy: try-catch
Validate before calling
null // a panic in library code cannot be pre-validated
Try / catch
// wrap InitTracing in a recover at the top-level CLI boundary; log and continue without tracing rather than crashing the run
defer func() { if r := recover(); r != nil { log.Warnf("tracing init crashed: %v", r) } }() Prevention
- Avoid mutating OTEL_* env vars concurrently with startup
- Ensure adequate memory at process start
- Pin recent compose versions that harden this path
When it happens
Trigger: os.Setenv(k, v) failing during the deferred restore loop: process out of memory allocating the new env string, or (on Windows) a concurrent env mutation race; reachable only after an OTEL endpoint was configured via Docker context and the client init path executed.
Common situations: Extreme memory pressure at tracing init; rare platform-level env failures; tests that mutate the environment from parallel goroutines while compose initializes tracing.
Related errors
- stashing env for %q: %w
- loading otel config from docker context metadata: %w
- initializing otel connection from docker context metadata: %
- failed to create resource: %w
- unexpected type for field %q: %T (expected: %T)
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/dcbdb3611eea1b7f.
Report an issue: GitHub.