"I'm saving the file, but the browser refuses to update." "HMR works once and then goes silent." If you've spent any meaningful time in Antigravity with Next.js or Vite, this scene probably feels familiar. I run into it constantly while maintaining the four sites that make up Dolice Labs, especially right after letting an AI agent rewrite a chunk of the codebase.
The frustrating part is that broken HMR isn't one bug — it's a category of bugs that span four different layers: the file watcher, the network path between server and browser, the build tool's own configuration, and Antigravity-specific behavior. Trying to fix it by intuition alone is a great way to lose an afternoon. What follows is the diagnostic order I actually use, plus the fixes for each layer. By the end, you should have at least one new "next thing to try" in your toolkit.
Start by classifying the symptom
HMR problems present in three fairly distinct ways, and each points to a different root cause. Before you touch a config file, decide which of these you're seeing:
- Symptom A: The terminal still prints
compiled successfully, but the browser stays silent. - Symptom B: Saving the file produces no terminal output at all — the build never starts.
- Symptom C: HMR works fine for the first few minutes, then suddenly stops.
In my experience, Symptom A is almost always a network-path issue, Symptom B is a file-watcher issue, and Symptom C is a resource-exhaustion issue. Picking the right starting point saves a surprising amount of time.
Symptom A: Only the browser is unresponsive
If the dev server is happily compiling but the browser doesn't pick up the change, the WebSocket channel that delivers HMR updates is being dropped somewhere along the way.
Open your browser's DevTools and check the Console and Network tabs. A red line like WebSocket connection to 'ws://localhost:3000/_next/webpack-hmr' failed is the smoking gun.
# Run inside Antigravity's integrated terminal
# Verify the HMR WebSocket port is actually listening
lsof -i -P | grep LISTEN | grep -E "3000|3001|24678"
# Expected output (example):
# node 12345 ... TCP *:3000 (LISTEN)There are three fixes I reach for, in order. First, when working over a remote workspace, Antigravity's port forwarder occasionally fails to proxy the WebSocket frame even though the HTTP traffic flows fine. Open the command palette, run "Forward a Port", and explicitly forward the HMR port. Second, browser extensions — especially aggressive ad blockers and CSP-rewriting extensions — sometimes drop WebSocket frames. Reproducing the issue in an Incognito window is the quickest way to rule this out. Third, with Vite specifically, pinning server.hmr.host and server.hmr.port in the config makes things dramatically more reliable across containers and remote sessions.
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
server: {
host: '0.0.0.0',
port: 5173,
hmr: {
// Make the WebSocket reachable across containers and proxies
host: 'localhost',
port: 5173,
protocol: 'ws',
},
},
});
// Expected behavior: browser DevTools console shows
// "[vite] connected." right after the page loads.Symptom B: The build never runs
If saving a file produces no log output at all, the file watcher isn't seeing your changes. This is the trickiest layer because the causes are diverse and often environment-specific.
The first suspect on Linux (including WSL and Devcontainers) is the inotify limit. Each watched file consumes one inotify slot, and large repositories blow through the default limit in seconds.
# Current limit
cat /proc/sys/fs/inotify/max_user_watches
# How many slots are currently in use
find /proc/*/fd -lname 'anon_inode:inotify' 2>/dev/null | wc -l
# Raise temporarily
sudo sysctl fs.inotify.max_user_watches=524288
# Persist across reboots
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf
sudo sysctl -pThe misleading "No space left on device" error you sometimes see is actually inotify exhaustion. I've documented the full recovery path in How to Fix Antigravity File Watcher ENOSPC Errors, which is worth a read if you hit that specific message.
If inotify isn't the issue, the next thing to verify is whether the file you're editing is actually inside the watched set. Next.js and Vite both exclude node_modules, .next, and .git by default, but custom next.config.js rules — or editing through symlinks — can quietly leave your file outside the watcher's scope.
// next.config.js — explicitly broaden the watcher
module.exports = {
webpack: (config, { dev }) => {
if (dev) {
// Follow symlinks; useful when monorepo packages are linked
config.watchOptions = {
followSymlinks: true,
// Polling is the brute-force fallback; CPU-heavy but reliable
poll: process.env.WATCH_POLL ? 1000 : false,
ignored: ['**/node_modules/**', '**/.next/**'],
};
}
return config;
},
};
// To confirm: run WATCH_POLL=1 npm run dev. If CPU rises but
// HMR finally fires on save, the watcher itself was the issue.Symptom C: Works at first, then dies
When HMR works for a while and then stops, it's almost always one of three things: a memory leak, an exhausted file watcher, or a dropped Antigravity session.
For memory, the OS-level tools are good enough — Activity Monitor on macOS, htop on Linux. If your node process climbs past 2 GB, you're entering the danger zone. Either start the dev server with NODE_OPTIONS=--max-old-space-size=4096, or build the habit of restarting it every couple of hours.
The Antigravity-specific failure mode here is interesting: when an AI agent commits a large batch of file edits, Antigravity's internal index can momentarily back up, which delays watcher notifications until it catches up. The fastest recovery I've found is running "Developer: Restart Extension Host" from the command palette — it's much lighter than restarting the whole editor and clears the backlog cleanly. If that doesn't help, your workspace index may be in worse shape; the recovery steps in Fixing a Stuck Antigravity Workspace Index cover the deeper cleanup.
A quick note on framework-specific quirks
Once you've ruled out the three big symptoms, a small number of framework-specific behaviors show up often enough to be worth knowing about.
With Next.js 14 and 16, App Router pages occasionally require a hard refresh after editing certain server components, because the server-side module graph and the client-side HMR boundary don't always agree on what changed. If a save updates the file but the page in the browser shows the old data, try a single Cmd-Shift-R (or Ctrl-Shift-R) to force a clean fetch — this isn't a bug in your setup, it's just how the boundary works for now.
With Vite, edits to files that are imported only by vite.config.ts won't trigger an HMR update because the config itself is loaded once at startup. Restart the dev server after changing build configuration; saving your config file alone will not produce visible behavior changes.
With Tailwind v3 and v4, missing class names sometimes look like an HMR failure when the real issue is the JIT scanner not picking up a newly added file path. Make sure your content (v3) or @source (v4) globs include any newly created directories.
These aren't bugs in Antigravity itself — they're framework-level edges that, when they line up with a separately broken piece of the stack, can make troubleshooting feel maddening. Knowing they exist helps you separate "my watcher is broken" from "my framework just doesn't reload this kind of edit."
Antigravity-specific gotchas
A few editor-specific things are easy to miss but routinely cause confusion.
First, Antigravity intentionally pauses parts of file-watching while an AI agent is mid-edit, to avoid spurious rebuilds during a large refactor. It resumes automatically when the agent finishes, but there's typically a brief window — a few seconds — when HMR feels broken. Wait, save again, and you'll usually find it's working.
Second, an overly broad files.watcherExclude in .antigravity/settings.json will silently exclude files you actually want to track. The fastest way to confirm this is to open "Toggle Developer Tools" in the editor and watch the console for save events; if no event fires when you save, the file is being filtered out.
Third, when you're using Antigravity's remote development extension, port forwarding is sometimes not persisted across restarts. Pinning remote.autoForwardPorts to true and listing your HMR port explicitly in remote.portsAttributes removes this whole class of problem.
A small ask before you go back to debugging
HMR breakage doesn't usually have a one-line fix — it has the classification I described above. Before you dive back in, the most useful thing you can do is write down whether you're seeing Symptom A, B, or C. That single decision prevents most of the time-sink that comes from randomly toggling settings.