Open jespertheend opened 1 year ago
Uncaught error formatting doesn't use error.stack
but the structured information error.stack
is made from. I suggest modifying error.message
instead or using error.cause
.
With that said, it should leave the 'true' stack trace untouched rather than deleting it. Seems that happens when error.stack
is set by the user before it's ever accessed, so Error.prepareStackTrace()
(our way of extracting the structured stack trace) isn't called. For example it wouldn't happen if you had e.stack;
between the first two lines. Not sure how to fix that.
Hmm, well my use case is that errors are serialized and sent over the network, and then thrown on another client with the modified stacktrace.
Leaving the full stack trace info in error.message
feels wrong in that case.
I have a similar use case to @jespertheend. I attempted to use Error.prepareStackTrace
which fails to do what I expect here. I outlined an example below:
class RemoteError extends Error {
constructor(message: string, server_callstack: string) {
const server_error_client_representation = new ServerErrorClientRepresentation(message, server_callstack)
super(`Remote error: ${message}`, { cause: server_error_client_representation })
}
}
class ServerErrorClientRepresentation extends Error {
constructor(message: string, server_callstack: string) {
super(message)
const prepare_stack_trace = Error.prepareStackTrace
Error.prepareStackTrace = () => {
console.log('prepareStackTrace called')
return server_callstack
}
const stack = this.stack
Error.prepareStackTrace = prepare_stack_trace
}
}
function server_code_foobar() {
throw new Error('foobar')
}
function server_code() {
try {
const result = server_code_foobar()
return JSON.stringify({ result })
} catch (e) {
return JSON.stringify({ error: {message: e.message, stack: e.stack} })
}
}
function client_code() {
// this happens over the wire in reality
const remote_result = JSON.parse(server_code())
if (remote_result.error) {
throw new RemoteError(remote_result.error.message, remote_result.error.stack)
} else {
return remote_result
}
}
try {
client_code()
} catch(client_side_error) {
console.log(client_side_error)
console.log()
console.log('---==== Deno Error Print ====---')
throw client_side_error
}
this snippet outputs the following:
prepareStackTrace called
Error: Remote error: foobar
at client_code (file:///home/andrew/Code/ts-rpc/scratchwork/prepare_stacktrace_repro.ts:39:11)
at file:///home/andrew/Code/ts-rpc/scratchwork/prepare_stacktrace_repro.ts:47:3
Caused by Error: foobar
at server_code_foobar (file:///home/andrew/Code/ts-rpc/scratchwork/prepare_stacktrace_repro.ts:24:9)
at server_code (file:///home/andrew/Code/ts-rpc/scratchwork/prepare_stacktrace_repro.ts:28:20)
at client_code (file:///home/andrew/Code/ts-rpc/scratchwork/prepare_stacktrace_repro.ts:37:36)
at file:///home/andrew/Code/ts-rpc/scratchwork/prepare_stacktrace_repro.ts:47:3
---==== Deno Error Print ====---
error: Uncaught (in promise) Error: Remote error: foobar
throw new RemoteError(remote_result.error.message, remote_result.error.stack)
^
at client_code (file:///home/andrew/Code/ts-rpc/scratchwork/prepare_stacktrace_repro.ts:39:11)
at file:///home/andrew/Code/ts-rpc/scratchwork/prepare_stacktrace_repro.ts:47:3
Caused by: Error: foobar
it looks like console.log
correctly prints the modified stacktrace, but deno's native exception (which in my case is important for E2E client/server tests) will ignore any custom stacktrace.
When you run this from the browser console, all browsers except Firefox display the stack trace:
But Deno actually removes the existing stack trace completely and only logs the error message: