Skip to content

fix: externalize @opentelemetry/api to prevent chunk colocation#16302

Draft
elliott-with-the-longest-name-on-github wants to merge 1 commit into
version-3from
elliott/externalize-opentelemetry-api
Draft

fix: externalize @opentelemetry/api to prevent chunk colocation#16302
elliott-with-the-longest-name-on-github wants to merge 1 commit into
version-3from
elliott/externalize-opentelemetry-api

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

closes #16288

When instrumentation.server.js and application code both import @opentelemetry/api, the bundler may colocate it into a shared chunk that also contains application modules. The compiled instrumentation entry then imports that chunk — evaluating application modules before Server.init() has called set_env(). Any module that reads $env/dynamic/private at module scope silently captures undefined.

This is a nastier variant of #14286: there the env modules are imported by instrumentation.server.ts itself; here instrumentation never touches $env — it merely imports @opentelemetry/api, and the chunking pulls in unrelated app modules.

What changed

  • Added @opentelemetry/api to ssr.external in the kit Vite plugin, so it stays as a bare import('@opentelemetry/api') in the build output rather than being bundled into a shared chunk.
  • Added @opentelemetry/api to adapter-node's external list (it's an optional peer dep, not in pkg.dependencies, so the existing regex wouldn't match it).

Why externalize?

  1. Prevents chunk colocation — no shared chunks between instrumentation and app code via this dep, so importing @opentelemetry/api from instrumentation can never cause app modules to evaluate.
  2. OTEL correctness — OpenTelemetry requires a single @opentelemetry/api module instance. The global tracer/propagation is set on that instance. Two bundled copies would mean instrumentation hooks are invisible to the SvelteKit runtime's tracer.

Other adapters don't need changes:

  • adapter-vercel serverless uses nodeFileTrace (copies deps, doesn't bundle) — the bare import resolves from node_modules.
  • adapter-vercel edge and adapter-netlify edge already bundle instrumentation as a separate single-file graph, and the edge bundler resolves the bare import into the bundle.
  • adapter-netlify serverless copies the kit server output directly via builder.writeServer.

@pkg-svelte-dev

pkg-svelte-dev Bot commented Jul 9, 2026

Copy link
Copy Markdown

Install the latest version of @sveltejs/kit from 2b5ae26:

pnpm add https://pkg.svelte.dev/@sveltejs/kit/c/2b5ae26db27ca58ff75eb17e4132987031743460

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

@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2b5ae26

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

This PR includes changesets to release 2 packages
Name Type
@sveltejs/kit Patch
@sveltejs/adapter-node 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

@elliott-with-the-longest-name-on-github elliott-with-the-longest-name-on-github force-pushed the elliott/externalize-opentelemetry-api branch from 90d3458 to a436ecb Compare July 9, 2026 20:42
When `instrumentation.server.js` and application code both import
`@opentelemetry/api`, the bundler may colocate it into a shared chunk
that also contains application modules. The compiled instrumentation
entry then imports that chunk — evaluating application modules before
`Server.init()` has called `set_env()`.

Externalizing `@opentelemetry/api` ensures:
- No shared chunks between instrumentation and app code (via this dep)
- A single module instance so OTEL hooks registered in instrumentation
  are visible to the SvelteKit runtime's tracer

Added to `ssr.external` in the kit Vite plugin and to adapter-node's
external list (since `@opentelemetry/api` is an optional peer dep, not
in `pkg.dependencies`, and wouldn't be matched by the existing regex).

Closes #16288
@Rich-Harris

Copy link
Copy Markdown
Member

Is this just treating a symptom? Couldn't the same thing happen with any module that was imported by both instrumentation.server.ts and app code?

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

Copy link
Copy Markdown
Contributor Author

You're correct. The only foolproof solution to this problem is externalizing all dependencies of instrumentation.server.ts to ensure they're not bundled with app code. In practice, though, tracing setup code other than @opentelemetry/api is very rarely something you're going to end up importing anywhere but your setup file.

Not sure what a better solution would look like...

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

Labels

None yet

Projects

None yet

2 participants