Skip to content
Go back

When Hot Reload Goes Cold

The problem

At some point in the last few weeks, my local development servers that watch for file changes (e.g., almost every project that has an npm [run] start command) have been failing to detect changed files until after some considerable delay — in some cases, as long as 5 minutes’ delay.

As Laurie Bream would say, this is untenable.

The non-solutions

I tried a bunch of things that seemed like possible fixes, based on my Googling:

One thing I tried did seem to work for some projects (but not all): Change the watch mode from filesystem events to polling.

The workaround

I got tired of trying to get the watch tools that were part of each ecosystem to work. I looked for a bigger hammer.

Enter watchexec, a standalone tool that watches for filesystem changes and can execute arbitrary shell commands or invoke binaries when changes are detected - and it also supports a polling mode.

For this blog, built using Astro / AstroPaper, the normal dev command as defined in package.json is:

{
  "name": "astro-paper",
  "type": "module",
  "version": "5.5.0",
  "scripts": {
    "dev": "astro dev",
    ...

With watchexec, I set up a dev:watch command (broken into multi-line for readability):

    "dev:watch": "watchexec 
                  --poll 5000ms
                  -w src 
                  -w astro.config.ts
                  -w package.json
                  -w tsconfig.json
                  -e md,mdx,ts,astro,json
                  --restart
                  --stop-signal SIGTERM
                  --print-events
                  --no-vcs-ignore
                  -- pnpm run dev",

Let’s go through that invocation of watchexec one flag at a time:

FlagPurpose
--poll 5000msUse polling mode instead of filesystem events. Check for file changes every 5 seconds (5000 milliseconds). This is the core workaround for dead watch mode.
-w srcWatch the src/ directory for changes.
-w astro.config.tsWatch the Astro config file. Changes to build config should trigger a restart.
-w package.jsonWatch dependencies and script definitions.
-w tsconfig.jsonWatch TypeScript configuration.
-e md,mdx,ts,astro,jsonOnly trigger on changes to these file types (markdown, MDX, TypeScript, Astro, JSON). Ignores unrelated changes like build artifacts.
--restartKill the previous process before starting a new one. Ensures clean state.
--stop-signal SIGTERMSend SIGTERM (graceful shutdown) to the process instead of SIGKILL. Allows the dev server to clean up.
--print-eventsLog which files changed and triggered the restart. Helpful for debugging watch behavior.
--no-vcs-ignoreDon’t skip files ignored by .gitignore. By default, watchexec respects .gitignore, but explicitly watching files with the -w flag overrides this anyway.
-- pnpm run devThe command to execute. Everything after -- is passed as the command to run.

Here’s the full scripts section of my package.json:

{
  "scripts": {
    "dev": "astro dev",
    "dev:watch": "watchexec --poll 5000ms -w src -w astro.config.ts -w package.json -w tsconfig.json -e md,mdx,ts,astro,json --restart --stop-signal SIGTERM --print-events --no-vcs-ignore -- pnpm run dev",
    "build": "astro check && astro build && pagefind --site dist && cp -r dist/pagefind public/",
    "preview": "astro preview",
    "sync": "astro sync",
    "astro": "astro",
    "format:check": "prettier --check .",
    "format": "prettier --write .",
    "lint": "eslint .",
    "new-post": "node scripts/new-post.js"
  }
}

If you’re encountering the same problem and you’re just about done wasting time on it, check out watchexec: https://github.com/watchexec/watchexec

Fixing the underlying problem

This doesn’t fix the underlying problem of my dev servers no longer triggering due to filesystem events, but it does unblock me and get my stuff working without needing to put further time into figuring something out that isn’t really that big of a deal.

If you’re encountering a similar issue on MacOS and you find an actual solution, please share your findings! I’ve already spent too much time trying to fix the problem and I’m satisfied with my workaround for now, but it does itch at me that I have to use a workaround because I can’t figure out the issue.

Here is some diagnostic information:

Machine: MacBook Pro M4 Max
OS: MacOS Version 15.1 (24B2083)
I have recently been plugging / unplugging external hard drives, formatting them, moving files around between them, making disk images of them, writing disk images to them, and just in general doing a lot of hard drive stuff with external drives. I think maybe at some point in this process, I disabled or otherwise broke the filesystem events that are dispatched when files get changed.


Share this post on: