Skip to content

Regression on image compilation with @astrojs/cloudflare adapter #16919

Description

@madonuko

Astro Info

Astro                    v6.3.8
Node                     v22.22.2
System                   Linux (x64)
Package Manager          bun
Output                   static
Adapter                  @astrojs/cloudflare
Integrations             none

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

No response

Describe the Bug

Issue

With @astrojs/cloudflare + having images in your site, you get:

04:11:50 [WARN] [build] Unable to generate optimized image for /_astro/terra-infra-graph.Den89wrj.png: Error: ENOENT: no such file or directory, open '/home/runner/work/docs/docs/dist/_astro/terra-infra-graph.Den89wrj.png'
04:11:50 [WARN] [build] Unable to generate optimized image for /_astro/terra.BDg5CSuk.webp: Error: ENOENT: no such file or directory, open '/home/runner/work/docs/docs/dist/_astro/terra.BDg5CSuk.webp'
Error generating image for /_astro/terra-infra-graph.Den89wrj.png: Error: ENOENT: no such file or directory, open '/home/runner/work/docs/docs/dist/_astro/terra-infra-graph.Den89wrj.png'
  Location:
    /home/runner/work/docs/docs/node_modules/astro/dist/core/build/generate.js:202:13
  Stack trace:
    at generatePages (file:///home/runner/work/docs/docs/node_modules/astro/dist/core/build/generate.js:202:13)
    at async Object.buildApp (file:///home/runner/work/docs/docs/node_modules/vite/dist/node/chunks/config.js:33898:5)
    at async viteBuild (file:///home/runner/work/docs/docs/node_modules/astro/dist/core/build/static-build.js:68:3)
    at async AstroBuilder.run (file:///home/runner/work/docs/docs/node_modules/astro/dist/core/build/index.js:199:7)
    at async build (file:///home/runner/work/docs/docs/node_modules/astro/dist/cli/build/index.js:29:3)
  Caused by:
  ENOENT: no such file or directory, open '/home/runner/work/docs/docs/dist/_astro/terra-infra-graph.Den89wrj.png'
    at async open (node:internal/fs/promises:640:25)
    at async loadImage (file:///home/runner/work/docs/docs/node_modules/astro/dist/assets/build/generate.js:240:11)
    at async generateImage (file:///home/runner/work/docs/docs/node_modules/astro/dist/assets/build/generate.js:69:28)
    at async run (file:///home/runner/work/docs/docs/node_modules/p-queue/dist/index.js:398:36)

Bisection

4cff3a107c3750ab5f0878a6b41836705282b771 is the first bad commit
commit 4cff3a107c3750ab5f0878a6b41836705282b771
Author: Matthew Phillips <matthewphillips@cloudflare.com>
Date:   Wed May 27 09:41:47 2026 -0400
    Skip SSR build for fully static Cloudflare sites (#16468)
    
    * Skip SSR build for fully static Cloudflare sites
    
    - Cloudflare adapter now passes through buildOutput from core instead of
      hardcoding 'server', allowing static sites to skip the SSR environment build.
    - Server islands plugin upgrades buildOutput from 'static' to 'server' when
      server:defer components are discovered during prerendering.
    
    * Use @cloudflare/vite-plugin pre-release from PR #12788
    
    Adds minimumReleaseAge exclusions for transitive Cloudflare deps.
    
    * Fix: upgrade buildOutput after prerender build, not during it
    
    Move server islands buildOutput upgrade from the vite plugin transform
    (which runs mid-prerender-build) to static-build.ts (after prerender
    completes). This avoids a directory path mismatch where the prerender
    output is written to the static path but manifest injection writes to
    the server path.
    
    * Fix: detect server islands via plugin API, mutate buildOutput after prerender
    
    - Expose hasServerIslands() on server islands plugin API
    - After prerender build, upgrade buildOutput to 'server' if server
      islands were discovered (so SSR build, manifest injection, and
      runtime all use correct server paths)
    - Capture prerenderOutputDir before mutation to avoid path mismatch
    - Simplify writeMutatedChunks to use getServerOutputDirectory
    - Update session wrangler config test for static build output path
    
    * Add preserveBuildServerDir, stop mutating buildOutput in static-build
    
    - Add preserveBuildServerDir adapter feature so adapters can control
      the server output directory for static builds (mirrors preserveBuildClientDir)
    - Cloudflare adapter sets preserveBuildServerDir: true
    - Extract hasServerIslands() from server islands plugin as a standalone
      exported function that accepts any BuildEnvironment
    - Replace buildOutput mutation in static-build.ts with a needsServerBuild()
      helper that checks settings.buildOutput or hasServerIslands()
    
    * Fix server islands guard to check for adapter, not buildOutput
    
    * Fix preview for static sites with SSR routes and internal route handling
    
    - Stop overwriting buildOutput after route scanning in preview command;
      let route scanning upgrade it from 'static' to 'server' when needed
    - Skip static preview server when adapter provides a previewEntrypoint
    - Use getClientOutputDirectory in static preview server to respect
      preserveBuildClientDir
    - Exempt internal routes (e.g. server islands) from getStaticPaths
      requirement and static path matching
    
    * Fix no-shadow lint error in server islands plugin
    
    * Add changesets for astro and cloudflare
    
    * Update astro changeset to minor with adapter usage example
    
    * Use @cloudflare/vite-plugin devOnly to skip SSR build for static sites
    
    Switch to @cloudflare/vite-plugin PR #13985 which adds a devOnly property.
    Set devOnly to a function that returns true when buildOutput is 'static',
    letting the CF plugin skip the SSR worker build and emit an assets-only
    wrangler config for fully static sites.
    
    * Update lockfile for @cloudflare/vite-plugin ^1.39.0
    
    * Update pnpm-workspace.yaml
    
    Co-authored-by: Emanuele Stoppa <estoppa@cloudflare.com>
    
    * Split changeset into separate patch entries
    
    * Update .changeset/cloudflare-static-buildoutput.md
    
    Co-authored-by: Armand Philippot <git@armand.philippot.eu>
    
    ---------
    
    Co-authored-by: Emanuele Stoppa <estoppa@cloudflare.com>
    Co-authored-by: Armand Philippot <git@armand.philippot.eu>
 .changeset/afraid-sheep-end.md                     |   5 +
 .changeset/cloudflare-preserve-build-dirs.md       |  18 ++
 .changeset/cloudflare-static-buildoutput.md        |   5 +
 .changeset/funky-peas-exist.md                     |   5 +
 .changeset/icy-days-sneeze.md                      |   5 +
 .changeset/two-views-clean.md                      |   5 +
 packages/astro/src/core/build/static-build.ts      |  30 ++-
 packages/astro/src/core/preview/index.ts           |  14 +-
 .../src/core/preview/static-preview-server.ts      |   3 +-
 packages/astro/src/core/render/params-and-props.ts |   2 +-
 packages/astro/src/core/render/route-cache.ts      |   4 +-
 packages/astro/src/core/routing/validation.ts      |   2 +-
 .../server-islands/vite-plugin-server-islands.ts   |  25 ++-
 packages/astro/src/prerender/utils.ts              |   9 +-
 packages/astro/src/types/public/integrations.ts    |   9 +
 packages/integrations/cloudflare/package.json      |   2 +-
 packages/integrations/cloudflare/src/index.ts      |  20 +-
 .../integrations/cloudflare/test/sessions.test.ts  |   4 +-
 pnpm-lock.yaml                                     | 230 ++++++++++++++++++---
 pnpm-workspace.yaml                                |   4 +
 20 files changed, 333 insertions(+), 68 deletions(-)
 create mode 100644 .changeset/afraid-sheep-end.md
 create mode 100644 .changeset/cloudflare-preserve-build-dirs.md
 create mode 100644 .changeset/cloudflare-static-buildoutput.md
 create mode 100644 .changeset/funky-peas-exist.md
 create mode 100644 .changeset/icy-days-sneeze.md
 create mode 100644 .changeset/two-views-clean.md
bisect found first bad commit

What's the expected result?

The site should build under the latest version of astro & @astrojs/cloudflare.

Link to Minimal Reproducible Example

https://github.com/madonuko/repro-astro-cf-image-bug

Participation

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    - P4: importantViolate documented behavior or significantly impacts performance (priority)pkg: astroRelated to the core `astro` package (scope)pkg: cloudflareRelated to the Cloudflare adapter

    Type

    No type

    Fields

    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