diff --git a/packages/server/src/middleware/error.js b/packages/server/src/middleware/error.js
index 01cf3a2b88..2361ef12de 100644
--- a/packages/server/src/middleware/error.js
+++ b/packages/server/src/middleware/error.js
@@ -6,19 +6,21 @@ import Youch from '@nuxtjs/youch'
export default ({ resources, options }) => function errorMiddleware(err, req, res, next) {
// ensure statusCode, message and name fields
- err.statusCode = err.statusCode || 500
- err.message = err.message || 'Nuxt Server Error'
- err.name = !err.name || err.name === 'Error' ? 'NuxtServerError' : err.name
- // We hide actual errors from end users, so show them on server logs
- if (err.statusCode !== 404) {
- consola.error(err)
+ const error = {
+ statusCode: err.statusCode || 500,
+ message: err.message || 'Nuxt Server Error',
+ name: !err.name || err.name === 'Error' ? 'NuxtServerError' : err.name
}
+ const errorFull = err instanceof Error ? err : typeof err === 'string'
+ ? new Error(err) : new Error(err.message || JSON.stringify(err))
+ errorFull.name = error.name
+ errorFull.statusCode = error.statusCode
const sendResponse = (content, type = 'text/html') => {
// Set Headers
- res.statusCode = err.statusCode
- res.statusMessage = err.name
+ res.statusCode = error.statusCode
+ res.statusMessage = error.name
res.setHeader('Content-Type', type + '; charset=utf-8')
res.setHeader('Content-Length', Buffer.byteLength(content))
res.setHeader('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
@@ -36,11 +38,15 @@ export default ({ resources, options }) => function errorMiddleware(err, req, re
// Use basic errors when debug mode is disabled
if (!options.debug) {
+ // We hide actual errors from end users, so show them on server logs
+ if (err.statusCode !== 404) {
+ consola.error(err)
+ }
// Json format is compatible with Youch json responses
const json = {
- status: err.statusCode,
- message: err.message,
- name: err.name
+ status: error.statusCode,
+ message: error.message,
+ name: error.name
}
if (isJson) {
sendResponse(JSON.stringify(json, undefined, 2), 'text/json')
@@ -53,7 +59,7 @@ export default ({ resources, options }) => function errorMiddleware(err, req, re
// Show stack trace
const youch = new Youch(
- err,
+ errorFull,
req,
readSourceFactory({
srcDir: options.srcDir,
diff --git a/packages/vue-app/template/client.js b/packages/vue-app/template/client.js
index 1b4d5bb3e4..21196fac77 100644
--- a/packages/vue-app/template/client.js
+++ b/packages/vue-app/template/client.js
@@ -36,11 +36,6 @@ Object.assign(Vue.config, <%= serialize(vue.config) %>)<%= isTest ? '// eslint-d
if (!Vue.config.$nuxt) {
const defaultErrorHandler = Vue.config.errorHandler
Vue.config.errorHandler = (err, vm, info, ...rest) => {
- const nuxtError = {
- statusCode: err.statusCode || err.name || 'Whoops!',
- message: err.message || err.toString()
- }
-
// Call other handler if exist
let handled = null
if (typeof defaultErrorHandler === 'function') {
@@ -53,10 +48,10 @@ if (!Vue.config.$nuxt) {
if (vm && vm.$root) {
const nuxtApp = Object.keys(Vue.config.$nuxt)
.find(nuxtInstance => vm.$root[nuxtInstance])
-
+
// Show Nuxt Error Page
if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
- vm.$root[nuxtApp].error(nuxtError)
+ vm.$root[nuxtApp].error(err)
}
}
@@ -68,7 +63,7 @@ if (!Vue.config.$nuxt) {
if (process.env.NODE_ENV !== 'production') {
console.error(err)
} else {
- console.error(err.message || nuxtError.message)
+ console.error(err.message || err)
}
}
Vue.config.$nuxt = {}
@@ -150,9 +145,7 @@ async function loadAsyncComponents(to, from, next) {
// Call next()
next()
} catch (err) {
- const error = err || {}
- const statusCode = (error.statusCode || error.status || (error.response && error.response.status) || 500)
- this.error({ statusCode, message: error.message })
+ this.error(err)
this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
next(false)
}
@@ -414,8 +407,6 @@ async function render(to, from, next) {
return this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
}
_lastPaths = []
- const errorResponseStatus = (error.response && error.response.status)
- error.statusCode = error.statusCode || error.status || errorResponseStatus || 500
globalHandleError(error)
diff --git a/packages/vue-app/template/index.js b/packages/vue-app/template/index.js
index 24648c1349..36aac1757b 100644
--- a/packages/vue-app/template/index.js
+++ b/packages/vue-app/template/index.js
@@ -7,7 +7,7 @@ import NuxtLink from './components/nuxt-link.js'
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
import Nuxt from './components/nuxt.js'
import App from '<%= appPath %>'
-import { setContext, getLocation, getRouteData } from './utils'
+import { setContext, getLocation, getRouteData, normalizeError } from './utils'
<% if (store) { %>import { createStore } from './store.js'<% } %>
/* Plugins */
@@ -90,7 +90,7 @@ async function createApp(ssrContext) {
error(err) {
err = err || null
app.context._errored = !!err
- if (typeof err === 'string') err = { statusCode: 500, message: err }
+ err = err ? normalizeError(err) : null
const nuxt = this.nuxt || this.$options.nuxt
nuxt.dateErr = Date.now()
nuxt.err = err
diff --git a/packages/vue-app/template/utils.js b/packages/vue-app/template/utils.js
index 4d1d6ff629..93b9783ae5 100644
--- a/packages/vue-app/template/utils.js
+++ b/packages/vue-app/template/utils.js
@@ -273,6 +273,23 @@ export function getQueryDiff(toQuery, fromQuery) {
return diff
}
+export function normalizeError(err) {
+ let message
+ if (!(err.message || typeof err === 'string')) {
+ try {
+ message = JSON.stringify(err, null, 2)
+ } catch (e) {
+ message = `[${err.constructor.name}]`
+ }
+ } else {
+ message = err.message || err
+ }
+ return {
+ message: message,
+ statusCode: (err.statusCode || err.status || (err.response && err.response.status) || 500)
+ }
+}
+
/**
* The main path matching regexp utility.
*
@@ -548,3 +565,4 @@ function formatQuery(query) {
return key + '=' + val
}).filter(Boolean).join('&')
}
+
diff --git a/test/e2e/basic.browser.test.js b/test/e2e/basic.browser.test.js
index c215a4f6fa..a62f553b28 100644
--- a/test/e2e/basic.browser.test.js
+++ b/test/e2e/basic.browser.test.js
@@ -158,7 +158,7 @@ describe('basic browser', () => {
test('/error', async () => {
await page.nuxt.navigate('/error')
- expect(await page.nuxt.errorData()).toEqual({ statusCode: 500 })
+ expect(await page.nuxt.errorData()).toEqual({ message: 'Error mouahahah', statusCode: 500 })
expect(await page.$text('.title')).toBe('Error mouahahah')
})
@@ -166,7 +166,7 @@ describe('basic browser', () => {
await page.nuxt.navigate('/error2')
expect(await page.$text('.title')).toBe('Custom error')
- expect(await page.nuxt.errorData()).toEqual({ message: 'Custom error' })
+ expect(await page.nuxt.errorData()).toEqual({ message: 'Custom error', statusCode: 500 })
})
test('/redirect-middleware', async () => {
diff --git a/test/fixtures/basic/pages/error-object.vue b/test/fixtures/basic/pages/error-object.vue
new file mode 100644
index 0000000000..28d30fbb18
--- /dev/null
+++ b/test/fixtures/basic/pages/error-object.vue
@@ -0,0 +1,8 @@
+
diff --git a/test/fixtures/basic/pages/error-string.vue b/test/fixtures/basic/pages/error-string.vue
new file mode 100644
index 0000000000..4b6c6d5256
--- /dev/null
+++ b/test/fixtures/basic/pages/error-string.vue
@@ -0,0 +1,8 @@
+
diff --git a/test/fixtures/basic/pages/error.vue b/test/fixtures/basic/pages/error.vue
index 2b2308b386..d6975dcc52 100644
--- a/test/fixtures/basic/pages/error.vue
+++ b/test/fixtures/basic/pages/error.vue
@@ -4,7 +4,7 @@
diff --git a/test/fixtures/spa/pages/error-handler-string.vue b/test/fixtures/spa/pages/error-handler-string.vue
new file mode 100644
index 0000000000..4b6c6d5256
--- /dev/null
+++ b/test/fixtures/spa/pages/error-handler-string.vue
@@ -0,0 +1,8 @@
+
diff --git a/test/fixtures/spa/plugins/error.js b/test/fixtures/spa/plugins/error.js
index 27bb917894..5c6de6b278 100644
--- a/test/fixtures/spa/plugins/error.js
+++ b/test/fixtures/spa/plugins/error.js
@@ -1,5 +1,5 @@
import Vue from 'vue'
Vue.config.errorHandler = function (err) {
- document.body.appendChild(document.createTextNode(`error handler triggered: ${err.message}`))
+ document.body.appendChild(document.createTextNode(`error handler triggered: ${err.message || err}`))
}
diff --git a/test/unit/basic.ssr.test.js b/test/unit/basic.ssr.test.js
index 8718601efa..665ef19fbc 100644
--- a/test/unit/basic.ssr.test.js
+++ b/test/unit/basic.ssr.test.js
@@ -174,6 +174,26 @@ describe('basic ssr', () => {
.rejects.toThrow('Error mouahahah')
})
+ test('/error-string', async () => {
+ let error
+ try {
+ await nuxt.server.renderRoute('/error-string', { req: {}, res: {} })
+ } catch (e) {
+ error = e
+ }
+ await expect(error).toEqual('fetch error!')
+ })
+
+ test('/error-object', async () => {
+ let error
+ try {
+ await nuxt.server.renderRoute('/error-object', { req: {}, res: {} })
+ } catch (e) {
+ error = e
+ }
+ await expect(error).toEqual({ error: 'fetch error!' })
+ })
+
test('/error status code', async () => {
await expect(rp(url('/error'))).rejects.toMatchObject({
statusCode: 500
@@ -201,7 +221,7 @@ describe('basic ssr', () => {
const { html, error } = await nuxt.server.renderRoute('/error2')
expect(html).toContain('Custom error')
expect(error.message).toContain('Custom error')
- expect(error.statusCode === undefined).toBe(true)
+ expect(error.statusCode).toBe(500)
})
test('/error2 status code', async () => {
diff --git a/test/unit/spa.test.js b/test/unit/spa.test.js
index 01d60ad17d..e882be4392 100644
--- a/test/unit/spa.test.js
+++ b/test/unit/spa.test.js
@@ -41,13 +41,21 @@ describe('spa', () => {
})
test('/error-handler', async () => {
- await renderRoute('/error-handler')
const { html } = await renderRoute('/error-handler')
expect(html).toMatch('error handler triggered: fetch error!')
})
+ test('/error-handler-object', async () => {
+ const { html } = await renderRoute('/error-handler')
+ expect(html).toMatch('error handler triggered: fetch error!')
+ })
+
+ test('/error-handler-string', async () => {
+ const { html } = await renderRoute('/error-handler-string')
+ expect(html).toMatch('error handler triggered: fetch error!')
+ })
+
test('/error-handler-async', async () => {
- await renderRoute('/error-handler-async')
const { html } = await renderRoute('/error-handler-async')
expect(html).toMatch('error handler triggered: asyncData error!')
})