> 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' ]
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.tsand see that the server exists correctly as this way the watcher is set before the server is closed.System Info
System: OS: macOS 14.6.1 CPU: (10) arm64 Apple M1 Pro Memory: 65.73 MB / 32.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 20.16.0 - ~/.nvm/versions/node/v20.16.0/bin/node Yarn: 1.22.19 - ~/.nvm/versions/node/v20.16.0/bin/yarn npm: 10.8.1 - ~/.nvm/versions/node/v20.16.0/bin/npm Browsers: Chrome: 129.0.6668.60 Safari: 18.0 npmPackages: vite: ^5.4.8 => 5.4.8Used 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:cssplugin: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-processreporter points to file system watchers set by vite:css plugin.Validations