Skip to content

@astrojs/cloudflare: advancedRouting fetchFile crashes build with "createGetEnv is not a function" #16956

@skezo

Description

@skezo

Astro Info

Astro                    v6.4.3
Vite                     v7.3.5
Node                     v22.22.3
System                   Linux (x64)
Output                   server
Adapter                  @astrojs/cloudflare (v13.6.1)

Describe the Bug

When experimental.advancedRouting is an object with a fetchFile pointing at a worker that imports cf from @astrojs/cloudflare/fetch, the build crashes:

(0 , __vite_ssr_import_0__.createGetEnv) is not a function
  at runInRunnerObject (workers/runner-worker/index.js:107:3)

With advancedRouting: true it builds fine, because virtual:astro:fetchable falls back to DefaultFetchHandler and never imports @astrojs/cloudflare/fetch.

It's a circular import. fetch.js runs setGetEnv(createGetEnv(globalEnv)) and createApp() at the top level. createApp comes from astro/app/entrypoint, which imports virtual:astro:fetchable, which re-exports my worker.ts, which imports @astrojs/cloudflare/fetch again. So fetch.js top-level runs while those modules are still mid-init, and the binding it reads is undefined.

You can tell it's the cycle and not bundling: nudging optimizeDeps just moves the error. Forcing @astrojs/cloudflare/fetch into optimizeDeps.include gives createGetEnv is not a function, exclude gives createApp is not a function. Same cycle, different binding read too early.

Workaround that fixes it, import cf lazily so worker.ts doesn't statically depend on it:

const { cf } = await import('@astrojs/cloudflare/fetch');

src/worker.ts that triggers it:

import { astro, FetchState } from 'astro/fetch';
import { cf } from '@astrojs/cloudflare/fetch';

export default {
  async fetch(request, env, ctx) {
    const state = new FetchState(request);
    const asset = await cf(state, env, ctx);
    if (asset) return asset;
    return astro(state);
  },
};

config:

experimental: { advancedRouting: { fetchFile: 'worker' } }

What's the expected result?

Build succeeds with a custom fetchFile that imports cf from @astrojs/cloudflare/fetch, like the docs pattern. Ideally fetch.js wouldn't fall over on the cycle, either don't run createApp() / setGetEnv() eagerly at module top level, or export createGetEnv as a hoisted function so it survives a half initialized cycle.

Link to Minimal Reproducible Example

https://github.com/skezo/astro-cf-repro

Participation

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    - P2: has workaroundAn edge case that only affects very specific usage, but has a trivial workaround (priority)pkg: cloudflareRelated to the Cloudflare adapter

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions