__filename not defined on dev with vite-plugin
Which Cloudflare product(s) does this pertain to?
Vite Plugin
What versions & operating system are you using?
Wrangler v4.12.0, Node v22.14.0, Vite Plugin v1.0.9
Please provide a link to a minimal reproduction
https://github.com/erictaylor/cloudflare-vite-plugin-bug
Describe the Bug
When running react-router dev with a @cloudflare/vite-plugin setup I get the following error:
[vite] Internal server error: __filename is not defined
at async ProxyServer.fetch (file:///Users/erictaylor/[redacted]/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/miniflare/src/workers/core/proxy.worker.ts:173:11)
However, I don't have any issues with preview or deployed builds. Everything works as expected in those cases, so the issue is only present on the development server.
Please provide any relevant error logs
No response
Thanks for the issue. Are you able to provide a minimal reproduction?
@jamesopstad its proven to be a bit difficult because I assume this is related to some dependency that I have.
I've been trying to get more detailed logging to figure out where this __filename reference is but that hasn't been easy. Anytime I try to start up the debug server I literally get no connection via the inspector.
Any thoughts on how to better deep dive where the error may be coming from?
If I can get a reproduction put together I will for sure.
Have you tried enabling nodejs_compat by adding "compatibility_flags": ["nodejs_compat"] to your Worker config? __filename suggests that there may be a dependency that relies on Node globals.
Otherwise, running DEBUG=vite:* npm run dev provides more detailed logging.
I have nodejs_compat already enabled.
Been doing some local testing and found the cause (though I don't fully understand the why right now).
I'm running this all in a monorepo setup. In the monorepo we have a UI library package that uses @mui/material.
We re-export all of the exports from that package, and then override exports from that package with our own customized implementations where needed.
Ex
export * from '@mui/material';
export { ThemeProvider } from './components/ThemeProvider';
I found by trail and error by removing components being rendered that ThemeProvider specifically is leading to this issue. If I rename the export from our UI library and import under a different name things start working.
export * from '@mui/material';
export { ThemeProvider as CustomThemeProvider } from './components/ThemeProvider';
I'm confused as to why exactly this is happening, and why it only happens on dev and not in production builds.
I can only guess that there is some difference in the build between esbuild (dev) and rollup (prod).
I also don't run into this issue when running without the @cloudflare/vite-plugin.
I'll see if I can build a reproduction now.
I'm also getting Uncaught ReferenceError: process is not defined errors in dev, but not in prod.
In prod builds I get Uncaught ReferenceError: Buffer is not defined errors.
Does the plugin expose any configuration options for controlling Node polyfills more closely?
Can you provide the contents of your Wrangler config file? In particular the compatibility_flags. Can you also check that your compatibility_date is recent (e.g. this year).
@petebacondarwin
wrangler.jsonc
/**
* For more details on how to configure Wrangler, refer to:
* https://developers.cloudflare.com/workers/wrangler/configuration/
*/
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "[redacted]",
"compatibility_date": "2025-04-04",
"compatibility_flags": ["nodejs_compat"],
"main": "./workers/app.ts",
"observability": {
"enabled": true
},
"env": {
"development": {
"preview_urls": true,
"routes": [
{
"pattern": "[redacted]",
"custom_domain": true
}
]
},
"staging": {
"preview_urls": false,
"routes": [
{
"pattern": "[redacted]",
"custom_domain": true
}
]
}
// TODO: Enable this in the future.
// "production": {
// "preview_urls": false,
// "routes": [
// {
// "pattern": "[redacted]",
// "custom_domain": true
// }
// ]
// }
}
}
vite.config.ts
import { execSync } from 'node:child_process';
import { cloudflare } from '@cloudflare/vite-plugin';
import { reactRouter } from '@react-router/dev/vite';
import autoprefixer from 'autoprefixer';
import { config as dotenvConfig } from 'dotenv';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import { version } from './package.json';
dotenvConfig({
path: '../../.env',
});
const GIT_COMMIT_SHORT_SHA = execSync('git rev-parse --short=7 HEAD').toString().trim();
export default defineConfig(({ mode }) => ({
build: {
minify: mode === 'production',
},
css: {
postcss: {
plugins: [autoprefixer],
},
},
define: {
'import.meta.env.APP_VERSION': JSON.stringify(version),
'import.meta.env.GIT_COMMIT_SHORT_SHA': JSON.stringify(GIT_COMMIT_SHORT_SHA),
},
plugins: [
cloudflare({
viteEnvironment: {
/** Do not change this name. It's to target the Vite SSR API environment. */
name: 'ssr',
},
}),
reactRouter(),
tsconfigPaths(),
],
server: {
port: 3000,
},
}));
Minimal reproduction can be found at: https://github.com/erictaylor/cloudflare-vite-plugin-bug
The first issue can be worked around by swapping the commented lines in:
packages/ui/src/index.tsapps/test/app/components/theme-provider.tsx
Interestingly enough though in this minimal repoduction, the error isn't specifically related to __filename but is something different.
Then the issue becomes that usage of node built-ins in the client build aren't polyfilled. It's not clear to me how to handle Node polyfilling for the client files.
Thanks @erictaylor - I will take a look this week.
Sorry I didn't make it to this, this week. Next week, I promise!
@petebacondarwin any update on this?
would like updates on this. really impacting developer observability when a route throws an error, you get no insight
Me thinking about this being fixed.
Seems related https://github.com/cloudflare/workers-sdk/issues/8755
Especially the error message:
Uncaught ReferenceError: __filename is not defined
at null.<anonymous>
(file:///...redacted.../node_modules/wrangler/import_meta_url.js:3:61)
in import_meta_url.js
at null.<anonymous> (server.js:113049:63) in __init
at null.<anonymous>
(file:///...redacted.../node_modules/wrangler/src/cli.ts:1:1)
in node_modules/wrangler/wrangler-dist/cli.js
at null.<anonymous> (server.js:18:53) in __require2
at null.<anonymous>
(file:///...redacted.../app/env.ts:1:36)
[code: 10021]
my error message (i'm using moonrepo)
theproject:start_web | at null.<anonymous> (server.js:140968:55) in import_meta_url.js
theproject:start_web | at null.<anonymous> (server.js:140938:66) in __init
theproject:start_web | at null.<anonymous> (server.js:322215:5) in ../../node_modules/wrangler/wrangler-dist/cli.js
theproject:start_web | at null.<anonymous> (server.js:18:53) in __require2
theproject:start_web | at null.<anonymous> (server.js:377816:32)
also get this error when attempting to deploy
@petebacondarwin checking in on the status of this. Anything we can do to help with diagnosis/reproducibility in order to get better stack traces on this error?
Really sorry. This has slipped down my todo list. Will get to it next week, hopefully.
@petebacondarwin just curious if there is any update here? Is their anything we can do to help?
Hey guys, our team cooked a workaround while this gets properly resolved, in order to debug what's trying to access __filename we're doing this:
// node-polyfills.ts
/**
* Node.js polyfills for Cloudflare Workers with enhanced debugging
* This module provides __filename and __dirname polyfills that capture
* the actual call sites for better debugging of Node.js compatibility issues.
*/
function getStackTrace(): string[] {
const stack = new Error().stack || ''
return stack.split('\n')
}
function extractCallerInfo(stack: string[]): { file: string; line: string; fullCaller: string } {
// Skip the first few lines of stack trace to get the actual caller
// - Line 0: "Error"
// - Line 1: at getStackTrace
// - Line 2: at getter function
// - Line 3: actual caller (what we want)
const callerLine = stack[3] || 'unknown'
// Try to extract file path from stack trace
// Format is usually: "at functionName (file:///path/to/file.js:line:col)"
const match = callerLine.match(/at .* \((.+?):(\d+):\d+\)/)
const file = match ? match[1] : 'unknown-file'
const line = match ? match[2] : 'unknown'
return { file, line, fullCaller: callerLine.trim() }
}
function logPolyfillAccess(
type: '__filename' | '__dirname',
callerInfo: { file: string; line: string; fullCaller: string }
) {
console.warn(`🚨 [POLYFILL] ${type} accessed from:`, {
file: callerInfo.file,
line: callerInfo.line,
caller: callerInfo.fullCaller,
timestamp: new Date().toISOString(),
})
// Also log the full stack for maximum debugging info
console.warn(`📍 [POLYFILL] Full stack trace for ${type}:`, getStackTrace())
}
// TypeScript declarations for the global polyfill properties
declare global {
var __polyfillFilename: string
var __polyfillDirname: string
}
/**
* Initialize polyfills - this sets up the debugging properties
*/
export function initNodePolyfills() {
// Only initialize once
if ('__polyfillFilename' in globalThis) {
return
}
console.log(
'🔧 [POLYFILL] Initializing Node.js __filename and __dirname polyfills for debugging...'
)
// Set up __filename polyfill
Object.defineProperty(globalThis, '__polyfillFilename', {
get(): string {
const stack = getStackTrace()
const callerInfo = extractCallerInfo(stack)
logPolyfillAccess('__filename', callerInfo)
// Try to return a meaningful filename
if (callerInfo.file && callerInfo.file !== 'unknown-file') {
// Convert file:// URLs to paths and clean up
const cleanFile = callerInfo.file.replace('file://', '').replace(/^\/+/, '/')
return cleanFile
}
return '/polyfilled-filename'
},
configurable: true,
enumerable: false,
})
// Set up __dirname polyfill
Object.defineProperty(globalThis, '__polyfillDirname', {
get(): string {
const stack = getStackTrace()
const callerInfo = extractCallerInfo(stack)
logPolyfillAccess('__dirname', callerInfo)
// Try to return a meaningful directory
if (callerInfo.file && callerInfo.file !== 'unknown-file') {
// Convert file:// URLs to paths and clean up
const cleanFile = callerInfo.file.replace('file://', '').replace(/^\/+/, '/')
const dir = cleanFile.includes('/')
? cleanFile.substring(0, cleanFile.lastIndexOf('/'))
: '/'
return dir
}
return '/polyfilled-dirname'
},
configurable: true,
enumerable: false,
})
console.log('🔧 [POLYFILL] Node.js polyfills initialized')
}
// Auto-initialize the polyfills when this module is imported
initNodePolyfills()
Then import it in entry.server.tsx
import './lib/node-polyfills'
and also include the polyfills in vite.config.ts
export default defineConfig((config) => ({
define: {
// Polyfill Node.js globals for Cloudflare Workers with debugging
__filename: 'globalThis.__polyfillFilename',
__dirname: 'globalThis.__polyfillDirname',
},
// ... rest of your config
})
It helped us figure out where in the code __filename or __dirname were trying to be accessed and properly patch.
@petebacondarwin @jamesopstad - a few of us using Zero have run into this. Here's a reproduction using Alchemy which wraps the cloudflare-vite plugin. Edit: We did some digging and may have been an issue introduced and fixed through https://github.com/rocicorp/mono/pull/4976. Will leave this up for now and the commit below should contain the error before Zero was updated..
https://github.com/austinm911/zero-starter/tree/79c9e17cf5d1491a395963c446bbb1740209d1f5
The wranger.json is located apps/web/.alchemy/local/wrangler.jsonc
I have the similar error __filename is not defined with nodejs_compat flag defined.
It is easy to reproduce, just setup a hello world worker, and return __filename in the response.
I also experienced this error when I used the create-start-app tool for creating TanStack Start projects.
11:27:35 PM [vite] Internal server error: __filename is not defined
at async ProxyServer.fetch (file:///Users/fulopkovacs/dev-projects/2025/test-3/node_modules/.pnpm/[email protected]/node_modules/miniflare/src/workers/core/proxy.worker.ts:174:11)
After some debugging I found that the issue was caused by the Drizzle add-on with the SqLite option selected: