Skip to content

Commit f878bdb

Browse files
committed
Sanitize server stack trace on errors
1 parent 20eb3f0 commit f878bdb

File tree

5 files changed

+68
-2
lines changed

5 files changed

+68
-2
lines changed

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

Lines changed: 9 additions & 2 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';
@@ -77,8 +78,14 @@ export class Server {
7778
handle: module.handle || (({ event, resolve }) => resolve(event)),
7879
handleError:
7980
module.handleError ||
80-
(({ status, error }) =>
81-
console.error((status === 404 && /** @type {Error} */ (error)?.message) || error)),
81+
(({ status, error, event }) => {
82+
const error_message = format_server_error(
83+
status,
84+
/** @type {Error} */ (error),
85+
event
86+
);
87+
console.error(error_message);
88+
}),
8289
handleFetch: module.handleFetch || (({ request, fetch }) => fetch(request)),
8390
reroute: module.reroute || (() => {}),
8491
transport: module.transport || {}

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,52 @@ 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+
const 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,
208+
(line) =>
209+
['+page.', '+layout.', '+server.'].find((snippet) => line.includes(snippet)) !== undefined
210+
);
211+
212+
if (last_line_from_src_code === -1) {
213+
// default to the whole stack trace
214+
return error.stack;
215+
}
216+
217+
return stack_trace.slice(0, last_line_from_src_code + 1).join('\n');
218+
}
219+
220+
/**
221+
* @param {any[]} array
222+
* @param {(item: any, index: number, array: any[]) => boolean} predicate
223+
*/
224+
export function find_last_index(array, predicate) {
225+
for (let i = array.length - 1; i >= 0; i--) {
226+
if (predicate(array[i], i, array)) {
227+
return i;
228+
}
229+
}
230+
return -1;
231+
}

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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<br />
2+
<form method="post">
3+
<button type="submit"> Trigger server error </button>
4+
</form>

0 commit comments

Comments
 (0)