Skip to content

Commit 202a615

Browse files
committed
Sanitize server stack trace on errors
1 parent 96ce0f9 commit 202a615

File tree

5 files changed

+64
-3
lines changed

5 files changed

+64
-3
lines changed

packages/kit/src/runtime/server/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { set_private_env, set_public_env, set_safe_public_env } from '../shared-
33
import { options, get_hooks } from '__SERVER__/internal.js';
44
import { DEV } from 'esm-env';
55
import { filter_private_env, filter_public_env } from '../../utils/env.js';
6+
import { format_server_error } from './utils.js'
67
import { prerendering } from '__sveltekit/environment';
78
import { set_read_implementation, set_manifest } from '__sveltekit/server';
89
import { set_app } from './app.js';
@@ -76,9 +77,10 @@ export class Server {
7677
this.#options.hooks = {
7778
handle: module.handle || (({ event, resolve }) => resolve(event)),
7879
handleError:
79-
module.handleError ||
80-
(({ status, error }) =>
81-
console.error((status === 404 && /** @type {Error} */ (error)?.message) || error)),
80+
module.handleError || (({ status, error, event }) => {
81+
const error_message = format_server_error(status, /** @type {Error} */ (error), event)
82+
console.error(error_message)
83+
}),
8284
handleFetch: module.handleFetch || (({ request, fetch }) => fetch(request)),
8385
reroute: module.reroute || (() => {}),
8486
transport: module.transport || {}

packages/kit/src/runtime/server/utils.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,50 @@ export function has_prerendered_path(manifest, pathname) {
180180
(pathname.at(-1) === '/' && manifest._.prerendered_routes.has(pathname.slice(0, -1)))
181181
);
182182
}
183+
184+
/**
185+
* Formats the error into a nice message with sanitized stack trace
186+
* @param {number} status
187+
* @param {Error} error
188+
* @param {import('@sveltejs/kit').RequestEvent} event
189+
*/
190+
export function format_server_error(status, error, event) {
191+
let formatted_text = `\x1b[1;31m[${status}] ${event.request.method} ${event.url.pathname}\x1b[0m\n`
192+
193+
if (status === 404) {
194+
return formatted_text + error.message
195+
}
196+
197+
return formatted_text + clean_up_stack_trace(error)
198+
}
199+
200+
/**
201+
* Provides a refined stack trace by excluding lines following the last occurrence of a line containing +page. +layout. or +server.
202+
* @param {Error} error
203+
*/
204+
export function clean_up_stack_trace(error) {
205+
const stack_trace = error.stack?.split('\n') ?? []
206+
const last_line_from_src_code = find_last_index(
207+
stack_trace, line => ['+page.', '+layout.', '+server.'].find(snippet => line.includes(snippet)) !== undefined
208+
)
209+
210+
if (last_line_from_src_code === -1) {
211+
// default to the whole stack trace
212+
return error.stack
213+
}
214+
215+
return stack_trace.slice(0, last_line_from_src_code + 1).join('\n')
216+
}
217+
218+
/**
219+
* @param {any[]} array
220+
* @param {(item: any, index: number, array: any[]) => boolean} predicate
221+
*/
222+
export function find_last_index(array, predicate) {
223+
for (let i = array.length - 1; i >= 0; i--) {
224+
if (predicate(array[i], i, array)) {
225+
return i;
226+
}
227+
}
228+
return -1;
229+
}

playgrounds/basic/src/routes/+page.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212

1313
<ul>
1414
<li><a href="images">images</a></li>
15+
<li><a href="force-error">force error</a></li>
1516
</ul>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const actions = {
2+
default: async () => {
3+
throw new Error('test')
4+
}
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<br>
2+
<form method="post">
3+
<button type="submit">
4+
Trigger server error
5+
</button>
6+
</form>

0 commit comments

Comments
 (0)