Skip to content

@astrojs/vercel 11.0.13: on-demand routes crash with "Cannot find native binding" (rolldown imported at runtime) #18328

Description

@claus

Astro Info

Astro                    v7.3.8
Vite                     v8.3.4
Node                     v24.18.0
System                   macOS (arm64)
Package Manager          npm
Output                   static
Adapter                  @astrojs/vercel (v11.0.13)
Integrations             none

If this issue only occurs in one browser, which browser is a problem?

No response

Describe the Bug

Since @astrojs/vercel 11.0.13, every on-demand route deployed to Vercel fails with 500 FUNCTION_INVOCATION_FAILED. Prerendered pages are unaffected. The function log shows:

Error: Cannot find native binding. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ...
    at file:///var/task/node_modules/rolldown/dist/shared/binding-DlR5d34p.mjs:603:34
  cause: Error: Cannot find module '@rolldown/binding-wasm32-wasi'

The generated server entry (entry.mjs) now contains import "rolldown";. This seems to come from #18263 ("Replace esbuild with Vite and Rolldown APIs"):

  1. dist/serverless/entrypoint.js, the runtime, imports constants such as ASTRO_PATH_HEADER from ../index.js.
  2. dist/index.js, the integration, imports ./serverless/middleware.js.
  3. In 11.0.13, serverless/middleware.js has a top-level import { rolldown } from "rolldown". In 11.0.12 it used a lazy await import("esbuild") inside the function.

So rolldown now gets loaded inside the function. Its platform-specific native binding is loaded dynamically, so file tracing doesn't copy it into the function: only @rolldown/pluginutils ends up in node_modules/@rolldown. The function crashes on start-up.

It also reproduces locally without Vercel, by importing the packaged function in isolation:

npm run build
cp -R .vercel/output/functions/_render.func /tmp/fn
cd /tmp/fn && node --input-type=module -e "await import('./.vercel/output/server/entry.mjs')"
# → Error: Cannot find native binding

Pinning @astrojs/vercel to 11.0.12 fixes it.

Repro:
https://github.com/claus/astro-vercel-rolldown-repo
https://astro-vercel-rolldown-repo.vercel.app/

What's the expected result?

On-demand routes render on Vercel, as with 11.0.12. The serverless runtime entry should not import build-time-only modules such as rolldown and @vercel/routing-utils. The shared constants could, for example, move into a small module with no build dependencies, which both index.js and entrypoint.js import.

Link to Minimal Reproducible Example

https://github.com/claus/astro-vercel-rolldown-repo

Participation

  • I am willing to submit a pull request for this issue.

Activity

  1. astro-factory commented on Oct 9, 2026

    @astro-factory
    Contributor

    Triage complete

    • Prepare the workspace: ready
    • Install dependencies: complete
    • Build the project: complete
    • Reproduce the issue: confirmed
    • Diagnose the cause: high confidence
    • Verify the diagnosis: bug
    • Attempt a fix: verified
    • Publish the result: set triage: fix pending

    • Reproduced: Yes. Reproduced locally by importing the packaged _render.func on its own with @astrojs/vercel@11.0.13. The same project on 11.0.12 works.
    • Exploration: Yes — View branch
    • Unit Test: Yes — packages/integrations/vercel/test/serverless-prerender.test.ts. This is a fixture build test, not a pure unit test.
    • Priority: P4: important. This is a regression in a patch release: every on-demand route deployed with @astrojs/vercel 11.0.13 crashes at startup. Pinning to 11.0.12 works around it.

    The serverless runtime entry (packages/integrations/vercel/src/serverless/entrypoint.ts) imports its header and param constants from the integration module ../index.js. That module imports ./serverless/middleware.js, and since #18263 that file has a top-level import { rolldown } from 'rolldown'. The built entry.mjs therefore ends up with import "rolldown" (and import "@vercel/routing-utils"). File tracing doesn't copy rolldown's platform-specific native binding, so the function throws "Cannot find native binding" when it starts.

    I found a potential fix for this issue. It's the approach you suggested: the shared constants move into a new module with no imports, src/serverless/constants.ts. entrypoint.ts and middleware.ts import from it, and index.ts re-exports the constants so the public exports don't change. I rebuilt your repro with the patched adapter: entry.mjs no longer imports rolldown or @vercel/routing-utils, and /ssr returns 200 from the isolated function copy. This still needs review by a maintainer.

    Try this fix

    You can test this fix right now without waiting for a release:

    # @astrojs/vercel
    npm i https://pkg.pr.new/@astrojs/vercel@f6b984d

    If this fixes your issue, please leave a comment letting us know (for example
    "confirmed, this fixes it"). A pull request will then be opened to get it merged.

    Full Triage Report

    Issue

    Title: @astrojs/vercel 11.0.13: on-demand routes crash with "Cannot find native binding" (rolldown imported at runtime)

    Environment (from issue): Astro v7.3.8, Vite v8.3.4, Node v24.18.0, macOS arm64, npm, @astrojs/vercel v11.0.13.

    Expected: On-demand routes render on Vercel, as they did with 11.0.12. The serverless runtime should not import build-time-only modules.

    Reproduction (reproduced locally, without Vercel)

    1. Cloned https://github.com/claus/astro-vercel-rolldown-repo, which has one static page and src/pages/ssr.astro with prerender = false. Installed @astrojs/vercel@11.0.13 and astro@7.3.8 with npm.
    2. Ran npx astro build. The build succeeds.
    3. .vercel/output/functions/_render.func/.vercel/output/server/entry.mjs contains:
      import "@vercel/routing-utils";
      import "rolldown";
      
      _render.func/node_modules/@rolldown/ contains only pluginutils. No native binding package is copied.
    4. cp -R .vercel/output/functions/_render.func /tmp/fn && cd /tmp/fn && node --input-type=module -e "await import('./.vercel/output/server/entry.mjs')" fails with:
      Error: Cannot find native binding. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ...
          at file:///tmp/fn/node_modules/rolldown/dist/shared/binding-DlR5d34p.mjs:603:34
        cause: Error: Cannot find module '@rolldown/binding-wasm32-wasi'
      

    Baseline: With @astrojs/vercel@11.0.12, entry.mjs only has import "@vercel/routing-utils"; and the isolated import succeeds.

    Diagnosis (confidence: high)

    Import chain:

    1. src/serverless/entrypoint.ts (lines 2-8), the runtime serverEntrypoint, imports ASTRO_LOCALS_HEADER, ASTRO_MIDDLEWARE_SECRET_HEADER, ASTRO_PATH_HEADER, ASTRO_PATH_PARAM and ASTRO_PATH_TOKEN_PARAM from '../index.js'.
    2. src/index.ts (the integration) has top-level imports of @vercel/routing-utils, tinyglobby, @vercel/nft, and ./serverless/middleware.js.
    3. src/serverless/middleware.ts (line 5) has a top-level import { rolldown } from 'rolldown'. This was added in 882dd98, "Replace esbuild with Vite and Rolldown APIs (Replace esbuild with Vite and Rolldown APIs #18263)". Before that, the code used a lazy await import('esbuild') inside generateEdgeMiddleware.
    4. The SSR bundle keeps these packages external. The constants get inlined, but the bare side-effect imports stay in entry.mjs. NFT copies rolldown but not its dynamically resolved native binding, so the function crashes on startup. Prerendered pages are unaffected.

    middleware.ts also imported from ../index.js, which created a circular import. @vercel/routing-utils was already in the runtime bundle in 11.0.12; it is pure JS, so it didn't break anything.

    Verification: bug (high confidence)

    Nothing in #18263, in the source comments, or in the docs suggests that loading rolldown in the deployed function was intended. #18263 swapped the build tooling, and the runtime coupling comes from incidental constant imports.

    Fix

    • New file with no imports: packages/integrations/vercel/src/serverless/constants.ts. It holds ASTRO_PATH_HEADER, ASTRO_PATH_PARAM, ASTRO_PATH_TOKEN_PARAM, ASTRO_LOCALS_HEADER, ASTRO_MIDDLEWARE_SECRET_HEADER and NODE_PATH, with their JSDoc moved unchanged.
    • entrypoint.ts and middleware.ts import from ./constants.js.
    • index.ts re-exports all six constants, so the public API doesn't change.
    • The rolldown import was not made lazy. After this change the runtime never reaches middleware.ts, so it isn't needed.

    Verification:

    • Repro: Rebuilt the repro with the patched dist. entry.mjs has no rolldown or @vercel/routing-utils imports, rolldown is no longer copied into the function, and default.fetch(new Request('http://localhost/ssr')) returns 200 with the rendered page.
    • New test: "server entry does not import build-time dependencies" in serverless-prerender.test.ts. It fails without the fix and passes with it.
    • Full vercel suite: 88 tests: 85 pass, 0 fail, 3 skipped.
    • Checks: Biome and tsc pass with no errors.

    Changeset: .changeset/vercel-runtime-rolldown.md (@astrojs/vercel patch).

    --- a/packages/integrations/vercel/src/serverless/entrypoint.ts
    +++ b/packages/integrations/vercel/src/serverless/entrypoint.ts
    @@ -5,7 +5,7 @@ import {
     	ASTRO_PATH_HEADER,
     	ASTRO_PATH_PARAM,
     	ASTRO_PATH_TOKEN_PARAM,
    -} from '../index.js';
    +} from './constants.js';
    --- a/packages/integrations/vercel/src/serverless/middleware.ts
    +++ b/packages/integrations/vercel/src/serverless/middleware.ts
    @@ -10,7 +10,7 @@ import {
     	ASTRO_PATH_TOKEN_PARAM,
     	NODE_PATH,
    -} from '../index.js';
    +} from './constants.js';
    --- a/packages/integrations/vercel/src/index.ts
    +++ b/packages/integrations/vercel/src/index.ts
    +import { ASTRO_PATH_PARAM, ASTRO_PATH_TOKEN_PARAM, NODE_PATH } from './serverless/constants.js';
    +export {
    +	ASTRO_LOCALS_HEADER,
    +	ASTRO_MIDDLEWARE_SECRET_HEADER,
    +	ASTRO_PATH_HEADER,
    +	ASTRO_PATH_PARAM,
    +	ASTRO_PATH_TOKEN_PARAM,
    +	NODE_PATH,
    +} from './serverless/constants.js';
     (constant definitions removed from index.ts, moved to serverless/constants.ts)

    Other adapters

    Netlify's src/index.ts also imports rolldown and esbuild. Its runtime files were not checked in depth, so it was left out of scope.

    This report was made by an LLM. The analysis may be wrong, and the potential fix might not work, but is intended as a starting point for exploring the issue.

  2. added
    - P4: importantViolate documented behavior or significantly impacts performance (priority)
    pkg: vercelRelated to Vercel adapter (scope)
    triage: fix pendingReporter needs to verify the triage bot fix works
    and removed on Oct 9, 2026
  3. claus commented on Oct 10, 2026

    @claus
    Author

    I updated my reproduction project to use https://pkg.pr.new/@astrojs/vercel@f6b984d and i think that didn't fix it.

  4. added
    triage: fix rejectedReporter says the proposed fix does not work
    and removed
    triage: fix pendingReporter needs to verify the triage bot fix works
    on Oct 10, 2026
  5. Souvlaki42 commented on Oct 11, 2026

    @Souvlaki42

    I have this issue in my own project too.
    https://github.com/Souvlaki42/my-website
    Pinning @astrojs/vercel to version 11.0.12 fixes it.
    Thanks.

  6. astro-factory commented on Oct 11, 2026

    @astro-factory
    Contributor

    Triage complete

    • Prepare the workspace: ready
    • Install dependencies: complete
    • Build the project: complete
    • Reproduce the issue: confirmed
    • Diagnose the cause: high confidence
    • Verify the diagnosis: bug
    • Attempt a fix: verified
    • Publish the result: set triage: fix pending

    • Reproduced: Yes. Reproduced locally, without Vercel, by importing the packaged _render.func on its own with @astrojs/vercel@11.0.13. The same project on 11.0.12 works.
    • Exploration: Yes — View branch
    • Unit Test: Yes — packages/integrations/vercel/test/serverless-prerender.test.ts. This is a fixture build test that checks the bundled function entry, not a pure unit test.
    • Priority: P4: important. This is a regression in a patch release: every on-demand route deployed with @astrojs/vercel 11.0.13 crashes when the function starts. Pinning to 11.0.12 works around it.

    The serverless runtime entry (packages/integrations/vercel/src/serverless/entrypoint.ts) imports its header and param constants from the integration module ../index.js. That module imports ./serverless/middleware.js, and since #18263 that file has a top-level import { rolldown } from 'rolldown'. The built entry.mjs therefore ends up with import "@vercel/routing-utils"; import "rolldown";. File tracing doesn't copy rolldown's platform-specific native binding into the function, so importing entry.mjs throws "Cannot find native binding".

    I found a potential fix for this issue. It's the approach you suggested:

    • The shared constants move into a new module with no imports, src/serverless/constants.ts.
    • entrypoint.ts and middleware.ts import from it. index.ts re-exports the constants, so the public exports don't change.
    • middleware.ts also loads rolldown lazily inside generateEdgeMiddleware, the way 11.0.12 loaded esbuild.

    I rebuilt your repro with the patched adapter. entry.mjs no longer mentions rolldown or @vercel/routing-utils, and the isolated function returns 200 for /ssr. A maintainer still needs to review it.

    @claus: the earlier preview f6b984d already removed the rolldown import. When I built your repo from your exact lockfile with npm ci, the isolated function served /ssr with 200, even with VERCEL=1 and the other Vercel build env vars set. Your live deploy still returns 500, and I can't see Vercel function logs from here, so I don't know why. Could you share the function log from that deployment, and try redeploying without the build cache? That would show whether it was a stale build or a second Vercel-only problem.

    Try this fix

    You can test this fix right now without waiting for a release:

    # @astrojs/vercel
    npm i https://pkg.pr.new/@astrojs/vercel@35a684c

    If this fixes your issue, please leave a comment letting us know (for example
    "confirmed, this fixes it"). A pull request will then be opened to get it merged.

    Full Triage Report

    Issue

    Title: @astrojs/vercel 11.0.13: on-demand routes crash with "Cannot find native binding" (rolldown imported at runtime)

    Environment (from issue): Astro v7.3.8, Vite v8.3.4, Node v24.18.0, macOS arm64, npm, @astrojs/vercel v11.0.13.

    Expected: On-demand routes render on Vercel, as they did with 11.0.12. The serverless runtime should not import build-only modules.

    Reproduction

    Sandbox: Linux x86_64, Node v22.23.2, npm 10.9.8. The repro is installed standalone with npm.

    1. Cloned https://github.com/claus/astro-vercel-rolldown-repo. It has one static page and src/pages/ssr.astro with prerender = false. Installed @astrojs/vercel@11.0.13 and astro@7.3.8.
    2. Ran npx astro build. The build succeeds.
    3. .vercel/output/functions/_render.func/.vercel/output/server/entry.mjs contains:
      import "@vercel/routing-utils";
      import "rolldown";
      
      _render.func/node_modules/@rolldown/ contains only pluginutils.
    4. Imported the function on its own:
      cp -R .vercel/output/functions/_render.func /tmp/fn && cd /tmp/fn
      node --input-type=module -e "await import('./.vercel/output/server/entry.mjs')"
      
      It fails with:
      Error: Cannot find native binding. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ...
          at file:///tmp/fn13/node_modules/rolldown/dist/shared/binding-DlR5d34p.mjs:603:34
        cause: Error: Cannot find module '@rolldown/binding-wasm32-wasi'
      

    Baseline: With @astrojs/vercel@11.0.12, entry.mjs only has import "@vercel/routing-utils";. The isolated function handles /ssr with 200.

    Preview f6b984d: entry.mjs has no rolldown or routing-utils import, and the isolated /ssr returns 200. This also holds with the reporter's exact lockfile (npm ci) and with VERCEL=1, VERCEL_DEPLOYMENT_ID and VERCEL_SKEW_PROTECTION_ENABLED set. The live deployment still returns 500 FUNCTION_INVOCATION_FAILED on /ssr. The cause is unknown without the function logs.

    Diagnosis (confidence: high)

    1. src/index.ts:140 sets serverEntrypoint: '@astrojs/vercel/entrypoint', which maps to dist/serverless/entrypoint.js. This module is bundled into the function.
    2. src/serverless/entrypoint.ts:2-8 imports ASTRO_* constants from '../index.js'.
    3. src/index.ts has top-level imports of build-only modules: @vercel/routing-utils, tinyglobby, ./lib/nft.js and ./serverless/middleware.js.
    4. src/serverless/middleware.ts:5 has a top-level import { rolldown } from 'rolldown', added in 882dd98cd (Replace esbuild with Vite and Rolldown APIs #18263). Before that, it loaded esbuild with a lazy await import('esbuild').
    5. The SSR build keeps npm packages external. The constants get inlined, but the bare side-effect imports stay in the bundle. At runtime rolldown loads its native binding dynamically, NFT can't trace it, and the function crashes on import.

    Verification: bug (high confidence)

    • This is a regression in a patch release. The changeset for Replace esbuild with Vite and Rolldown APIs #18263 describes it as an internal refactor.
    • The coupling between the runtime and index.ts dates from 2024. No comment or code shows it was intended, and it was harmless until a native-binding package was imported at the top level.
    • A second user reports the same problem.

    Fix

    • New src/serverless/constants.ts with no imports. It holds ASTRO_PATH_HEADER, ASTRO_PATH_PARAM, ASTRO_PATH_TOKEN_PARAM, ASTRO_LOCALS_HEADER, ASTRO_MIDDLEWARE_SECRET_HEADER and NODE_PATH, moved verbatim with their docs.
    • entrypoint.ts and middleware.ts import from ./constants.js. This also removes the circular middleware.ts → index.ts import.
    • index.ts imports and re-exports the constants, so the public API is unchanged.
    • middleware.ts loads rolldown lazily inside generateEdgeMiddleware (const { rolldown } = await import('rolldown')).
    • Changeset: .changeset/vercel-runtime-no-rolldown.md (@astrojs/vercel: patch).

    Fix verification

    • I packed the patched adapter and installed it into a copy of the reporter's repro.
    • The function entry.mjs has 0 occurrences of rolldown, and node_modules no longer contains rolldown or @vercel/*.
    • The isolated /ssr returns 200.
    • Full vercel suite: 88 tests, 85 pass, 0 fail, 3 pre-existing skips. This includes the edge-middleware and ISR edge-middleware tests, which use rolldown.
    • Biome, tsc and eslint are clean.

    Test

    it('server entry does not import build-only dependencies') in test/serverless-prerender.test.ts reads the _render.func handler named in .vc-config.json. It asserts the handler has no "rolldown" or "@vercel/routing-utils" import. Without the fix it fails, because the entry contains import "@vercel/routing-utils";\nimport "rolldown";. With the fix it passes.

    Alternatives considered

    • Only lazy-loading rolldown: this fixes the crash, but the runtime would still bundle the integration module and @vercel/routing-utils.
    • sideEffects/externals config: indirect and fragile compared with removing the import.

    This report was made by an LLM. The analysis may be wrong, and the potential fix might not work, but is intended as a starting point for exploring the issue.

  7. added
    triage: fix pendingReporter needs to verify the triage bot fix works
    and removed on Oct 11, 2026
  8. claus commented on Oct 11, 2026

    @claus
    Author

    Confirmed, this fixes it.

    Vercel's build cache was indeed hanging on to the buggy package.

  9. added
    triage: fix verifiedReporter confirmed the triage bot fix works
    and removed
    triage: fix pendingReporter needs to verify the triage bot fix works
    on Oct 11, 2026
  10. astro-factory commented on Oct 11, 2026

    @astro-factory
    Contributor

    The fix has been verified! A pull request has been created: #18339

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

Metadata

Metadata

Assignees

Labels

- P4: importantViolate documented behavior or significantly impacts performance (priority)pkg: vercelRelated to Vercel adapter (scope)triage: fix verifiedReporter confirmed the triage bot fix works

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions