skip to content

In Vue 3.5, what does app.config.throwUnhandledErrorInProduction change, and when would you turn it on?

level: seniorimportance: nice to knowfreq 20%

answer

  1. dev throws, prod logs
  2. global listeners never see it
  3. rethrow in production
  4. irrelevant once errorHandler exists
  5. useful for monitoring and SSR

basics

~20 s

Vue rethrows unhandled errors in development but only logs them in production. Setting throwUnhandledErrorInProduction to true makes production rethrow too, so page-level error listeners and server code see them; it has no effect once errorHandler is set.

solid answer

~40 s

When an error escapes component code and nothing handled it, and no `app.config.errorHandler` is set, Vue's default differs by mode: development **throws** it so it is noticed, production **logs** it with `console.error` so users are not disrupted. The production default has a cost: a logged error never reaches `window`-level error or unhandled-rejection listeners, so monitoring that relies on them misses errors that only happen in production. Vue 3.5's `app.config.throwUnhandledErrorInProduction = true` makes production rethrow as development does. It is only consulted on that unhandled path, so an app that already reports through `errorHandler` gains nothing from it. It also fits server rendering, where failing loudly is often better than rendering a partial page.

code

ts · 14 lines
ts
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// Strategy: no errorHandler; let unhandled errors reach page-level listeners.
app.config.throwUnhandledErrorInProduction = true // Vue 3.5+

window.addEventListener('error', (e) => send(e.error))
window.addEventListener('unhandledrejection', (e) => send(e.reason))

app.mount('#app')

declare function send(err: unknown): void

go deeper

for a junior

Recall that development throws unhandled errors while production only logs them, and that Vue 3.5 added a flag to change that.

for a middle

Explain the order: component hooks, then errorHandler, then the default path where this flag applies.

for a senior

Pick one production reporting strategy, understand why logged errors bypass global listeners, and weigh the user impact of rethrowing.

for a principal

Set the organisation's error policy for Vue apps, balancing resilience for users against visibility, consistently across client and server rendering.

## The default behaviour Vue wraps the application code it calls, such as render functions, `setup()`, lifecycle hooks, event handlers, watchers and directive hooks. When one of them throws, Vue first gives component-level error hooks a chance, then calls `app.config.errorHandler` if one is configured. Only when **neither** handled it does Vue fall back to its default, which depends on the build: | Build | Default for an unhandled error | |---|---| | Development | print `Unhandled error during execution of ...` as a warning, then **rethrow** | | Production | **log** with `console.error` and continue | | Production with `throwUnhandledErrorInProduction: true` | **rethrow**, as development does | The development choice makes errors impossible to miss. The production choice minimises user impact: one failing handler should not take down the rest of the page. ## Why the production default can hurt Many error-monitoring setups install listeners on the page for uncaught errors and unhandled promise rejections. A `console.error` call is neither, so those listeners never fire. The result is a class of bugs that: - never throws in development, because the triggering data only exists in production; - is caught and merely logged by Vue in production; - therefore never reaches the monitoring dashboard. The flag, added in Vue 3.5, closes that gap by letting the error propagate as a real exception. Depending on where it was thrown, it surfaces as an uncaught error, for example from a DOM event listener, or as an unhandled rejection, for example from an update that ran in Vue's promise-based scheduler flush. ## How it interacts with errorHandler The flag is read only on the final, unhandled path. The order is: 1. component-level error hooks up the parent chain; 2. `app.config.errorHandler`, if set, after which Vue stops; 3. the default: log or throw, where the flag decides production behaviour. So there are two valid production strategies: - **Report explicitly**: set `errorHandler` and send errors to monitoring from it. The flag is then irrelevant. - **Let them bubble**: leave `errorHandler` unset, set the flag, and rely on page-level listeners. Setting both is harmless but misleading: the flag never runs because `errorHandler` always returns first. ## Server rendering On the server, a rendering error that is only logged can yield an incomplete HTML response that looks successful. With the flag enabled the error propagates out of the render call, so server code can return an error status or a fallback page. That is the case the option's own source comment calls out as a reason to throw. ## Risks of turning it on - A rethrown error can abort the operation that was running, for example leaving a partially updated UI, where the default would have logged and carried on. - Third-party scripts listening for global errors may react to Vue errors they previously never saw. - Error volume in monitoring may jump, revealing long-hidden bugs: good, but plan for it. ## A worked example A checkout button's click handler calls a formatting function that throws for one currency only production customers use: 1. Development never sees the error, because test data never uses that currency. 2. In production, with no `errorHandler`, Vue logs the error with `console.error` and the button simply does nothing. 3. The page-level listener that feeds monitoring never fires, so the dashboard stays green while orders drop. 4. With `throwUnhandledErrorInProduction: true`, the error escapes as an uncaught exception, the listener reports it, and the team sees it quickly. An `errorHandler` that reports would have caught it as well, which is why the flag is best seen as the minimal switch for teams that already rely on page-level listeners. ## Checklist - Confirm the Vue version is 3.5 or later. - Decide on one strategy: `errorHandler` reporting or rethrow plus page-level listeners. - Enable the flag in the same place as the rest of `app.config`, before `mount()`. - Verify with a deliberately failing component in a production build, not a development build, since development already throws.

  • If app.config.errorHandler is set, does throwUnhandledErrorInProduction still rethrow?
    No. Once `errorHandler` exists, Vue calls it and returns, so the error is considered handled and the default path that reads the flag never runs. To rethrow in that setup, the handler itself would have to throw, which Vue then catches and logs as an error in the handler.
  • Why does Vue log rather than throw in production by default?
    An exception escaping a render or an update can leave parts of the page broken for the user. Logging keeps the rest of the app running. The trade-off is visibility: logged errors bypass page-level error listeners, which is why the 3.5 option exists.

saying these in an interview costs you the question

  • Vue rethrows unhandled errors in production by default
  • The flag also rethrows errors that errorHandler has already handled
  • console.error calls reach window error listeners
  • The option exists in every Vue 3 version
  • Enabling it has no effect on what users experience