You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If addWatchFile() is called by a Vite plugin after the server is closed, the watcher will be set, preventing Node.js process from exiting.
For example, if vite:css was in the middle of transforming a file when the server is closed, vite:css may call addWatchFile() , which will hang the process.
> npx vite
Waiting 1s to ensure Vite server is fully started
Port 5173 is in use, trying another one...
Port 5174 is in use, trying another one...
Port 5175 is in use, trying another one...
Port 5176 is in use, trying another one...
Port 5177 is in use, trying another one...
Port 5178 is in use, trying another one...
Port 5179 is in use, trying another one...
Port 5180 is in use, trying another one...
Port 5181 is in use, trying another one...
Port 5182 is in use, trying another one...
VITE v5.4.8 ready in 99 ms
➜ Local: http://localhost:5183/
➜ Network: use --host to expose
➜ press h + enter to show help
Starting index.html transform
Waiting 1s to ensure transform() is called before closing the server
Delaying CSS transform by 4s to ensure server is closed before transform completes
[ 'TTYWrap', 'TTYWrap', 'TTYWrap', 'TCPServerWrap', 'Timeout' ]
Close the server
Active resources before CSS transform:
[ 'TTYWrap', 'TTYWrap', 'TTYWrap' ]
Deprecation Warning: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0.
More info: https://sass-lang.com/d/legacy-js-api
Active resources after CSS transform:
[ 'CloseReq', 'FSReqCallback', 'TTYWrap', 'TTYWrap', 'TTYWrap' ]
Server is closed BUT node.js process is still running because of the watcher set by vite:css plugin
[ 'CloseReq', 'FSReqCallback', 'TTYWrap', 'TTYWrap', 'TTYWrap' ]
Logs with `--debug`
> npx vite --debug
vite:config bundled config file loaded in 41.34ms +0ms
vite:config using resolved config: {
vite:config plugins: [
vite:config 'vite:optimized-deps',
vite:config 'vite:watch-package-data',
vite:config 'vite:pre-alias',
vite:config 'alias',
vite:config 'vite:modulepreload-polyfill',
vite:config 'vite:resolve',
vite:config 'vite:html-inline-proxy',
vite:config 'vite:css',
vite:config 'vite:esbuild',
vite:config 'vite:json',
vite:config 'vite:wasm-helper',
vite:config 'vite:worker',
vite:config 'vite:asset',
vite:config 'make-scss-take-longer',
vite:config 'vite:wasm-fallback',
vite:config 'vite:define',
vite:config 'vite:css-post',
vite:config 'vite:worker-import-meta-url',
vite:config 'vite:asset-import-meta-url',
vite:config 'vite:dynamic-import-vars',
vite:config 'vite:import-glob',
vite:config 'vite:client-inject',
vite:config 'vite:css-analysis',
vite:config 'vite:import-analysis'
vite:config ],
vite:config optimizeDeps: {
vite:config holdUntilCrawlEnd: true,
vite:config force: undefined,
vite:config esbuildOptions: { preserveSymlinks: false }
vite:config },
vite:config server: {
vite:config preTransformRequests: true,
vite:config host: undefined,
vite:config sourcemapIgnoreList: [Function: isInNodeModules$1],
vite:config middlewareMode: false,
vite:config fs: {
vite:config strict: true,
vite:config allow: [Array],
vite:config deny: [Array],
vite:config cachedChecks: undefined
vite:config }
vite:config },
vite:config configFile: '/Users/mak13180/site/esri/vite-watcher-hangs/vite-app/vite.config.ts',
vite:config configFileDependencies: [
vite:config '/Users/mak13180/site/esri/vite-watcher-hangs/vite-app/vite.config.ts'
vite:config ],
vite:config inlineConfig: {
vite:config root: undefined,
vite:config base: undefined,
vite:config mode: undefined,
vite:config configFile: undefined,
vite:config logLevel: undefined,
vite:config clearScreen: undefined,
vite:config optimizeDeps: { force: undefined },
vite:config server: { host: undefined }
vite:config },
vite:config root: '/Users/mak13180/site/esri/vite-watcher-hangs/vite-app',
vite:config base: '/',
vite:config decodedBase: '/',
vite:config rawBase: '/',
vite:config resolve: {
vite:config mainFields: [ 'browser', 'module', 'jsnext:main', 'jsnext' ],
vite:config conditions: [],
vite:config extensions: [
vite:config '.mjs', '.js',
vite:config '.mts', '.ts',
vite:config '.jsx', '.tsx',
vite:config '.json'
vite:config ],
vite:config dedupe: [],
vite:config preserveSymlinks: false,
vite:config alias: [ [Object], [Object] ]
vite:config },
vite:config publicDir: '/Users/mak13180/site/esri/vite-watcher-hangs/vite-app/public',
vite:config cacheDir: '/Users/mak13180/site/esri/vite-watcher-hangs/vite-app/node_modules/.vite',
vite:config command: 'serve',
vite:config mode: 'development',
vite:config ssr: {
vite:config target: 'node',
vite:config optimizeDeps: { noDiscovery: true, esbuildOptions: [Object] }
vite:config },
vite:config isWorker: false,
vite:config mainConfig: null,
vite:config bundleChain: [],
vite:config isProduction: false,
vite:config css: { lightningcss: undefined },
vite:config esbuild: { jsxDev: true },
vite:config build: {
vite:config target: [ 'es2020', 'edge88', 'firefox78', 'chrome87', 'safari14' ],
vite:config cssTarget: [ 'es2020', 'edge88', 'firefox78', 'chrome87', 'safari14' ],
vite:config outDir: 'dist',
vite:config assetsDir: 'assets',
vite:config assetsInlineLimit: 4096,
vite:config cssCodeSplit: true,
vite:config sourcemap: false,
vite:config rollupOptions: {},
vite:config minify: 'esbuild',
vite:config terserOptions: {},
vite:config write: true,
vite:config emptyOutDir: null,
vite:config copyPublicDir: true,
vite:config manifest: false,
vite:config lib: false,
vite:config ssr: false,
vite:config ssrManifest: false,
vite:config ssrEmitAssets: false,
vite:config reportCompressedSize: true,
vite:config chunkSizeWarningLimit: 500,
vite:config watch: null,
vite:config commonjsOptions: { include: [Array], extensions: [Array] },
vite:config dynamicImportVarsOptions: { warnOnError: true, exclude: [Array] },
vite:config modulePreload: { polyfill: true },
vite:config cssMinify: true
vite:config },
vite:config preview: {
vite:config port: undefined,
vite:config strictPort: undefined,
vite:config host: undefined,
vite:config https: undefined,
vite:config open: undefined,
vite:config proxy: undefined,
vite:config cors: undefined,
vite:config headers: undefined
vite:config },
vite:config envDir: '/Users/mak13180/site/esri/vite-watcher-hangs/vite-app',
vite:config env: { BASE_URL: '/', MODE: 'development', DEV: true, PROD: false },
vite:config assetsInclude: [Function: assetsInclude],
vite:config logger: {
vite:config hasWarned: false,
vite:config info: [Function: info],
vite:config warn: [Function: warn],
vite:config warnOnce: [Function: warnOnce],
vite:config error: [Function: error],
vite:config clearScreen: [Function: clearScreen],
vite:config hasErrorLogged: [Function: hasErrorLogged]
vite:config },
vite:config packageCache: Map(1) {
vite:config 'fnpd_/Users/mak13180/site/esri/vite-watcher-hangs/vite-app' => {
vite:config dir: '/Users/mak13180/site/esri/vite-watcher-hangs/vite-app',
vite:config data: [Object],
vite:config hasSideEffects: [Function: hasSideEffects],
vite:config webResolvedImports: {},
vite:config nodeResolvedImports: {},
vite:config setResolvedCache: [Function: setResolvedCache],
vite:config getResolvedCache: [Function: getResolvedCache]
vite:config },
vite:config set: [Function (anonymous)]
vite:config },
vite:config createResolver: [Function: createResolver],
vite:config worker: { format: 'iife', plugins: '() => plugins', rollupOptions: {} },
vite:config appType: 'spa',
vite:config experimental: { importGlobRestoreExtension: false, hmrPartialAccept: false },
vite:config getSortedPlugins: [Function: getSortedPlugins],
vite:config getSortedPluginHooks: [Function: getSortedPluginHooks]
vite:config } +3ms
Waiting 1s to ensure Vite server is fully started
vite:deps Hash is consistent. Skipping. Use --force to override. +0ms
Port 5173 is in use, trying another one...
Port 5174 is in use, trying another one...
Port 5175 is in use, trying another one...
Port 5176 is in use, trying another one...
Port 5177 is in use, trying another one...
Port 5178 is in use, trying another one...
Port 5179 is in use, trying another one...
Port 5180 is in use, trying another one...
Port 5181 is in use, trying another one...
Port 5182 is in use, trying another one...
VITE v5.4.8 ready in 126 ms
➜ Local: http://localhost:5183/
➜ Network: use --host to expose
➜ press h + enter to show help
Starting index.html transform
Waiting 1s to ensure transform() is called before closing the server
vite:resolve 1.12ms /src/main.ts -> /Users/mak13180/site/esri/vite-watcher-hangs/vite-app/src/main.ts +0ms
vite:load 1.05ms [fs] /src/main.ts +0ms
vite:resolve 0.41ms ./style.scss -> /Users/mak13180/site/esri/vite-watcher-hangs/vite-app/src/style.scss +8ms
vite:import-analysis 1.87ms [1 imports rewritten] src/main.ts +0ms
vite:transform 6.65ms /src/main.ts +0ms
vite:load 1.18ms [fs] /src/style.scss +8ms
Delaying CSS transform by 4s to ensure server is closed before transform completes
[ 'TTYWrap', 'TTYWrap', 'TTYWrap', 'TCPServerWrap', 'Timeout' ]
Close the server
Active resources before CSS transform:
[ 'TTYWrap', 'TTYWrap', 'TTYWrap' ]
Deprecation Warning: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0.
More info: https://sass-lang.com/d/legacy-js-api
Active resources after CSS transform:
[ 'CloseReq', 'FSReqCallback', 'TTYWrap', 'TTYWrap', 'TTYWrap' ]
Server is closed BUT node.js process is still running because of the watcher set by vite:css plugin
[ 'CloseReq', 'FSReqCallback', 'TTYWrap', 'TTYWrap', 'TTYWrap' ]
Explanation of the issue
Multiple built-in Vite plugins are setting watchers for files. For example, vite:css plugin:
These watchers are set even if server.watcher.close() was called.
These watchers in turn prevent the Node.js process from exiting, hanging it.
Solutions:
In server.close(), if there are pending requests, after the requests are processed, close all watchers again if any were open.
OR Make PluginContext.addWatchFile() a noop after the watcher is closed
OR Add a check for watcher being closed before calling addWatchFile (error prone)
Real-world example
I am starting Vite dev server inside Vitest global setup file. (Vite dev server is used for Puppeteer)
The Vite dev server is closed in the global teardown file.
If some test fails, the teardown is called early, while Vite dev server might still be in the process of transforming a CSS file.
The vite:css plugin may set a file watcher, prevent Vitest from exiting.
Vitest prints this message:
close timed out after 10000ms
Tests closed successfully but something prevents Vite server from exiting
You can try to identify the cause by enabling "hanging-process" reporter. See https://vitest.dev/config/#reporters
The hanging-process reporter points to file system watchers set by vite:css plugin.
Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to vuejs/core instead.
Until the bug is fixed, my workaround is to add this to my vitest global teardown script:
/** * Workaround for https://github.com/vitejs/vite/issues/18224 */constintervalTime=100;constinterval=setInterval(()=>{voiddevServer.watcher.close();},intervalTime);// Prevent interval from keeping the process openinterval.unref();
maxpatiiuk
changed the title
Vite can hang the node.js process if server is exited during file transform
Vite can hang the node.js process if server is closed during file transform
Sep 29, 2024
Describe the bug
If
addWatchFile()
is called by a Vite plugin after the server is closed, the watcher will be set, preventing Node.js process from exiting.For example, if vite:css was in the middle of transforming a file when the server is closed, vite:css may call
addWatchFile()
, which will hang the process.Reproduction
https://github.com/maxpatiiuk/vite-watcher-hangs
Steps to reproduce
Clone this repository
Install dependencies
Run the Vite server
See that the Node.js process does not exit after the server is closed
vite.config.ts
and see that the server exists correctly as this way the watcher is set before the server is closed.System Info
Used Package Manager
npm
Logs
Logs without `--debug`
Logs with `--debug`
Explanation of the issue
Multiple built-in Vite plugins are setting watchers for files. For example,
vite:css
plugin:vite/packages/vite/src/node/plugins/css.ts
Line 384 in 0474550
These watchers are set even if
server.watcher.close()
was called.These watchers in turn prevent the Node.js process from exiting, hanging it.
Solutions:
server.close()
, if there are pending requests, after the requests are processed, close all watchers again if any were open.PluginContext.addWatchFile()
a noop after the watcher is closedReal-world example
I am starting Vite dev server inside Vitest global setup file. (Vite dev server is used for Puppeteer)
The Vite dev server is closed in the global teardown file.
If some test fails, the teardown is called early, while Vite dev server might still be in the process of transforming a CSS file.
The vite:css plugin may set a file watcher, prevent Vitest from exiting.
Vitest prints this message:
The
hanging-process
reporter points to file system watchers set by vite:css plugin.Validations
The text was updated successfully, but these errors were encountered: