Skip to content

fix: eagerly initialise dynamic env vars from process.env at module-eval time#16303

Draft
elliott-with-the-longest-name-on-github wants to merge 1 commit into
elliott/externalize-opentelemetry-apifrom
elliott/eager-env-init
Draft

fix: eagerly initialise dynamic env vars from process.env at module-eval time#16303
elliott-with-the-longest-name-on-github wants to merge 1 commit into
elliott/externalize-opentelemetry-apifrom
elliott/eager-env-init

Conversation

@elliott-with-the-longest-name-on-github

@elliott-with-the-longest-name-on-github elliott-with-the-longest-name-on-github commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Previously, $app/env/private and $app/env/public dynamic variable values were only populated when Server.init() called set_env(). If any module reading these values was evaluated before Server.init() ran — for example via bundler chunk colocation with instrumentation.server.js (see #16288) — the values would be silently undefined.

What changed

builder.instrument() now accepts an env option. When provided, the generated facade creates a separate init module that imports set_env from the env module and calls it with the platform's env before instrumentation is imported. Since static imports are evaluated in order, this ensures env is populated (and validated) before any instrumentation or application code runs.

Each adapter passes the appropriate env source:

  • adapter-node, adapter-vercel (serverless): process.env
  • adapter-cloudflare: env from cloudflare:workers (available at module scope)
  • adapter-vercel (edge), adapter-netlify (serverless): env init added to their existing generateText facades

If required env vars are missing, set_env will throw a clear validation error at startup — this is intentional, as the app cannot function without them.

Why?

This eliminates the entire class of "captured undefined" bugs regardless of when a module evaluates, and works across all platforms (not just Node-like ones) because each adapter provides its own env source.

Stacked on top of #16302 (which externalizes @opentelemetry/api to prevent the primary colocation vector). Both fixes are complementary:

@pkg-svelte-dev

pkg-svelte-dev Bot commented Jul 9, 2026

Copy link
Copy Markdown

Install the latest version of @sveltejs/kit from 3508875:

pnpm add https://pkg.svelte.dev/@sveltejs/kit/c/3508875d9d7fd77328f4c541ee97774f4d73e9bd

Open in pkg.svelte.dev: https://pkg.svelte.dev/repos/kit/pr/16303

@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3508875

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@sveltejs/kit Patch
@sveltejs/adapter-node Patch
@sveltejs/adapter-cloudflare Patch
@sveltejs/adapter-vercel Patch
@sveltejs/adapter-netlify Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@svelte-docs-bot

Copy link
Copy Markdown

Previously, `$app/env/private` and `$app/env/public` dynamic variable values were only
populated when `Server.init()` called `set_env()`. If any module reading these values
was evaluated before `Server.init()` ran (e.g. via bundler chunk colocation with
`instrumentation.server.js`), the values would be silently `undefined`.

`builder.instrument()` now accepts an `env` option. When provided, the generated
facade creates a separate init module that imports `set_env` and calls it with the
platform's env before instrumentation is imported. This ensures dynamic env vars are
populated (and validated) before any instrumentation or application code is evaluated.

Adapters that have env available at module-load time pass the appropriate expression:
- adapter-node, adapter-vercel (serverless): `process.env`
- adapter-cloudflare: `env` from `cloudflare:workers`
- adapter-vercel (edge), adapter-netlify (serverless): env init via `generateText`

If required env vars are missing, `set_env` will throw — this is intentional, as the
app cannot function without them.
@SobolievOleksii

Copy link
Copy Markdown

Tested this branch against the repro for #16288 (adapter-node + src/instrumentation.server.js): the server crashes at startup —

build/__sveltekit_env_init.js:2
import { set_env } from './env.js';
SyntaxError: The requested module './env.js' does not provide an export named 'set_env'

The facade written in builder.js hardcodes import { set_env } from './env.js' relative to the entrypoint, but for adapter-node build/env.js is the adapter's own runtime helper (export { env, env_prefix, timeout_env }) — and kit's env module with set_env doesn't end up at a stable relative path after the adapter's bundling step. Full details in #16288 (comment) — happy to re-test after an adjustment.

@elliott-with-the-longest-name-on-github

Copy link
Copy Markdown
Contributor Author

Yeah, this is still a draft, not ready yet 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

imports from $env are undefined when imported in src/instrumentation.server.js

2 participants