Skip to content

fix: ensure clientDir exists before writing _headers file#292

Merged
james-elicx merged 1 commit intocloudflare:mainfrom
Divkix:fix/headers-enoent
Mar 6, 2026
Merged

fix: ensure clientDir exists before writing _headers file#292
james-elicx merged 1 commit intocloudflare:mainfrom
Divkix:fix/headers-enoent

Conversation

@Divkix
Copy link
Copy Markdown
Contributor

@Divkix Divkix commented Mar 6, 2026

Summary

  • The closeBundle hook writes _headers to clientDir (dist/client/) without creating the directory first. Only distDir (dist/) is checked earlier in the hook.
  • When build cache is invalidated (e.g., chunk graph changes), dist/client/ may not exist yet, causing an ENOENT error on writeFileSync.
  • Adds fs.mkdirSync(clientDir, { recursive: true }) before the write. recursive: true is a no-op if the directory already exists, so this is safe on warm builds.

Reproduction

  1. Delete dist/ entirely (simulating cache invalidation)
  2. Run bun run build
  3. Build fails with ENOENT: no such file or directory, open 'dist/client/_headers'

Fix

Single line addition at packages/vinext/src/index.ts — ensures the directory exists before writing.

Test plan

  • Clean build (rm -rf dist && bun run build) succeeds without ENOENT
  • Warm build (re-run bun run build with existing dist/) still succeeds (no-op mkdirSync)
  • dist/client/_headers file is generated with correct cache headers

The `closeBundle` hook writes `_headers` to `clientDir` (`dist/client/`)
without creating the directory first. Only `distDir` (`dist/`) is checked
earlier. When build cache is invalidated, `dist/client/` may not exist yet,
causing an ENOENT error on `writeFileSync`.

Add `fs.mkdirSync(clientDir, { recursive: true })` before the write.
`recursive: true` is a no-op if the directory already exists, so this is
safe on warm builds.
Copilot AI review requested due to automatic review settings March 6, 2026 04:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a build-time failure in the vinext:cloudflare-build closeBundle hook by ensuring the client output directory exists before writing the Cloudflare _headers file (used for immutable caching of content-hashed assets).

Changes:

  • Create dist/client/ (clientDir) recursively before writing dist/client/_headers to prevent ENOENT on cold builds / cache invalidation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@james-elicx
Copy link
Copy Markdown
Collaborator

/bigbonk review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Mar 6, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@292

commit: d1373a6

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, correct fix. The closeBundle hook guards distDir existence at line 3457 but never ensures clientDir (dist/client/) exists before writing _headers. All reads from clientDir earlier in the hook are individually guarded by existsSync, so they degrade gracefully — but the writeFileSync at line 3574 is the one unguarded write and will throw ENOENT when the directory is missing.

mkdirSync with recursive: true is the right call — idempotent on warm builds, creates the directory on cold builds. No concerns.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented Mar 6, 2026

Approved. The fix is correct and minimal:

  • The closeBundle hook checks that distDir (dist/) exists at line 3457, but clientDir (dist/client/) is never guaranteed to exist before the writeFileSync at line 3574.
  • All earlier reads from clientDir are individually guarded by existsSync, so they handle a missing directory gracefully — but the _headers write was the one unguarded operation.
  • mkdirSync with recursive: true is idempotent (no-op when the directory already exists), so it's safe on both cold and warm builds.

github run

@james-elicx james-elicx enabled auto-merge (squash) March 6, 2026 13:47
@james-elicx james-elicx merged commit bd19a5f into cloudflare:main Mar 6, 2026
17 of 18 checks passed
@Divkix Divkix deleted the fix/headers-enoent branch March 6, 2026 20:06
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.

3 participants