Skip to content

Env service snapshots process.env at init, causing stale reads and silent set() #22451

Description

@jerome-benoit

Description

The Env service (src/env/index.ts) captures a shallow copy of process.env at layer initialization via InstanceState.make(() => Effect.succeed({ ...process.env })). This design causes two bugs:

  1. Stale reads: Env.get(key) / Env.all() read from the frozen copy. Env vars set after initialization — by auth flows (e.g. OPENCODE_CONSOLE_TOKEN in config.ts), provider credential injection (e.g. AWS_BEARER_TOKEN_BEDROCK in provider.ts), or external SDKs — are invisible through the Env API.

  2. Silent set(): Env.set(key, value) mutates the internal copy only, never process.env. External code (AWS SDK, child processes, @ai-sdk/* providers) that reads process.env directly never sees these writes. This is the semantic mismatch flagged in Env.set does not update process.env, breaking provider SDKs #11481.

Evidence in the codebase: provider.ts contains two workaround blocks with TODO comments explicitly documenting this bug:

// TODO: Using process.env directly because Env.set only updates a process.env shallow copy,
// until the scope of the Env API is clarified (test only or runtime?)

Root cause: The snapshot was introduced in commit 90f39bf67 to prevent parallel test runs from contaminating each other's env vars. The per-instance InstanceState cache (added in 6a9907901) preserved this snapshot approach. However, process.env is a process-global — per-directory isolation via InstanceState is architecturally incorrect for env vars, and the test isolation goal was already served by existing save/restore patterns in test/preload.ts and per-test beforeEach/afterEach hooks.

Steps to reproduce

  1. Start opencode with no ANTHROPIC_API_KEY set
  2. Authenticate via the console (sets OPENCODE_CONSOLE_TOKEN at runtime)
  3. Observe that Env.get("OPENCODE_CONSOLE_TOKEN") returns undefined (reads stale snapshot)
  4. Or: set an env var via Env.set("KEY", "value") and observe that process.env.KEY is unchanged

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions