fix(server): provide fresh ConfigProvider per HttpApi listener#25726
Merged
kitlangton merged 1 commit intodevfrom May 4, 2026
Merged
fix(server): provide fresh ConfigProvider per HttpApi listener#25726kitlangton merged 1 commit intodevfrom
kitlangton merged 1 commit intodevfrom
Conversation
Effect's `ConfigProvider.fromEnv()` spreads `process.env` into a frozen trie at construction time, and the default `ConfigProvider` is cached on a module-singleton `Context.Reference` after the first read. Without an explicit override, every later `Server.listen()` keeps observing the env snapshot from the first config read in the process. Install a fresh `ConfigProvider` inside `buildLayer` so each listener's `Config.string(...)` resolves against the current `process.env`. This keeps `ServerAuth.Config` (and any other env-backed config) consistent with `Flag.*` reads in production and across the test suite. Parameterize the no-auth listen test over both backends so the effect-httpapi path is exercised; previously it was hardcoded to Hono.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
Alternative to #25671. Same goal (fix the no-auth listen test on the HttpApi backend, unblock the Hono-deletion PR), smaller surface, no override of
ServerAuth.Config.defaultLayer.Root cause
Effect's
ConfigProvider.fromEnv()(source) is not a live reader ofprocess.env:And the default
ConfigProvideris cached on a module-singletonContext.Referenceafter the first read:So the first
Config.string("OPENCODE_SERVER_PASSWORD")in the process:defaultValuefactory.fromEnv()once → snapshot ofprocess.envat that moment.Subsequent
Server.listen()calls — even with their own freshmemoMap— still query the same cached, staleConfigProvider. Mutatingprocess.envafter that first read is invisible to Effect Config.I verified this empirically with a 4-line repro:
Fix
One line in
listenHttpApi'sbuildLayer:Each
Server.listen()evaluatesfromEnv()fresh, snapshotting currentprocess.env, and provides thatConfigProviderfor the listener's layer build. The cached default is never consulted by the listener path.ServerAuth.Configkeeps its plainConfigService.Servicedeclaration. NodefaultLayeroverride, noLayer.syncoverFlag.*, no test-helper churn. TheConfig.string(...)field declarations stay meaningful.Compared to #25671
defaultLayer)auth.tsdefaultLayer, drops Effect Config wiring)ConfigServiceabstractionFlag.*instead ofprocess.envprocess.envperlisten()(was: once per process)Equivalent outcome for the failing test, smaller blast radius. The architectural concern raised in #25671's "Concerns" section ("is overriding
ConfigService.defaultLayerfor one specific service the right shape") goes away — this PR doesn't override it.Tests
httpapi-listen.test.ts: parameterized "tickets optional when auth disabled" over both backends — locks ineffect-httpapiparity. 6/6 pass with this fix; 1/2 fails on dev (theeffect-httpapivariant — confirmed empirically).httpapi-raw-route-auth.test.tsandhttpapi-ui.test.ts: unchanged. They already injectConfigProvider.fromUnknown(...)perapp()call, which works and continues to work.Out of scope: pre-existing test pollution
While verifying this fix, I noticed the trio (
session+provider+workspace) fails 16/17 on dev when run together —beforeEach/afterEachhook timeouts cascade. Same files pass alone. Confirmed pre-existing on dev (no fix applied). With this fix the count moves to 17/17 — one marginal extra fail from timing perturbation. Unrelated toConfigProvider; likely theServer.Default()lazy +disposeAllInstances+ DB teardown not draining cleanly when stacked. Worth a separate issue.