A deep dive into a subtle production bug that occurs when Vite and esbuild minify your React app — and your Axios error checks suddenly stop working.
When you build your React app with Vite, you expect it to behave the same way it does in development. But sometimes, production introduces mysterious behavior — things that should work just don’t.
Recently, I ran into one of those “this makes no sense” bugs.
Everything worked perfectly with npm run dev.
But after running npm run build, my Axios interceptor simply stopped catching errors.
Let’s look at what happened — and how you can avoid wasting hours debugging the same issue.
Here’s the interceptor in question:
import axios, { AxiosError } from 'axios';
axios.interceptors.response.use(
response => response,
error => {
if (error instanceof AxiosError) {
console.warn('Request failed:', error.message);
}
return Promise.reject(error);
}
);
In development, this worked flawlessly.
In production, the same check failed silently — no warnings, no logs.
When Vite builds your app, it uses esbuild (or Rollup + Terser) to minify your JavaScript bundle.
That minification process doesn’t just remove whitespace and rename variables — it can also rename class constructors.
So the original class from Axios:
class AxiosError extends Error {}
may be turned into something like:
class e extends Error {}
Now, when you check:
if (error instanceof AxiosError)
…it no longer works.
The class AxiosError doesn’t exist under that name anymore in your production bundle.
Try logging the error class name in both environments:
console.log(error.constructor.name);
Development:
AxiosError
Production:
e
Boom 💥 — your friendly neighborhood minifier has just renamed your class.
Axios provides a safe and reliable method to detect Axios-specific errors:
if (axios.isAxiosError(error)) {
console.warn('Axios error detected:', error.message);
}
This works consistently in both dev and production, regardless of how your classes are named.
If you really want to preserve class names (e.g., for debugging or instanceof checks), configure Vite to keep them:
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
minify: 'terser',
terserOptions: {
keep_classnames: true,
keep_fnames: true,
},
},
});
Or if you rely on esbuild’s default minifier:
esbuild: {
keepNames: true,
},
Note that this may slightly increase bundle size, but it ensures that class names like AxiosError stay intact.
Minification is both a blessing and a curse. It makes your app lighter and faster — but if your logic depends on class names or function names, you might be in trouble once production minifies your code.
Key lessons:
instanceof checks for third-party error classes.axios.isAxiosError).This bug is a perfect example of how “smart” build tools can cause “dumb” headaches.
When something works locally but not after a build, it’s often not your logic — it’s the optimizer.
Next time your production code acts differently, check whether the minifier is quietly rewriting your assumptions.
And remember: the smallest byte saved can sometimes cost you the biggest debugging session.
Information