Skip to content

Asserts (and probably other Bounce.isSystem) loose stack trace #4531

Closed
@legraphista

Description

@legraphista

Runtime

node.js

Runtime version

22.9.0

Module version

@hapi/[email protected]

Last module version without issue

unknown

Used with

standalone

Any other relevant information

When throwing an unhandled exception, the stack is available in the boom object in onPreResponse, but not for "system" exceptions like AssertionError


Culprit:

response = Boom.badImplementation(err);

The error is passed as message instead of data

https://github.com/hapijs/boom/blob/01a4996dc6e62949aa7a98ca75b2d0dcd8a4a0a8/lib/index.js#L74
Boom catches it as error, but Hoek.clone loses the stack trace ❗

> messageOrError.stack
'AssertionError [ERR_ASSERTION]: Hello World
    at handler (/home/stefan/[redacted]/src/index.ts:26:9)
    at exports.Manager.execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/toolkit.js:57:29)
    at internals.handler (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/handler.js:46:48)
    at exports.execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/handler.js:31:36)
    at Request._lifecycle (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/request.js:370:68)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async Request._execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/request.js:280:9)'
> Hoek.clone(messageOrError).stack
undefined

https://github.com/hapijs/hoek/blob/0f0a14eae94d28b4826e3bb4b2071b4eccbd398f/lib/clone.js#L91-L94
when key === 'stack', the descriptor is retrieved and applied to newObj, but descriptor.get() is undefined

> Object.getOwnPropertyDescriptor(new Error('hello'), 'stack').get()
undefined

What are you trying to achieve or the steps to reproduce?

export const hapiServer = new Server({
    port: 3000,
    address: "0.0.0.0"
});

hapiServer.ext('onPreResponse', (request, h) => {
    const response = request.response;
    if (response instanceof Error) {
        console.error(request.info.id, response);
    }
    return h.continue;
});

hapiServer.route({
    method: 'GET',
    path: '/assert',
    handler: (request, h) => {
        assert(false, 'Hello World');
    }
});

hapiServer.route({
    method: 'GET',
    path: '/throw',
    handler: (request, h) => {
        throw new Error('Hello World');
    }
});

await hapiServer.start()

What was the result you got?

curl 127.0.0.1:3000/assert

1729274024376:something:416933:m2f13fpr:10000 [AssertionError: Hello World] {
  generatedMessage: false,
  code: 'ERR_ASSERTION',
  actual: false,
  expected: true,
  operator: '==',
  isBoom: true,
  isServer: true,
  data: null,
  output: [Object],
  isDeveloperError: true
}
curl localhost:3000/throw

1729274081766:something:416933:m2f13fpr:10001 Error: Hello World
    at handler (/home/stefan/[redacted]/src/index.ts:34:15)
    at exports.Manager.execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/toolkit.js:57:29)
    at internals.handler (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/handler.js:46:48)
    at exports.execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/handler.js:31:36)
    at Request._lifecycle (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/request.js:370:68)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async Request._execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/request.js:280:9) {
  isBoom: true,
  isServer: true,
  data: null,
  output: {
    statusCode: 500,
    payload: {
      statusCode: 500,
      error: 'Internal Server Error',
      message: 'An internal server error occurred'
    },
    headers: {}
  }
}

What result did you expect?

curl 127.0.0.1:3000/assert

1729274228005:something:419617:m2f183s8:10000 AssertionError [ERR_ASSERTION]: Hello World
    at handler (/home/stefan/[redacted]/src/index.ts:26:9)
    at exports.Manager.execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/toolkit.js:57:29)
    at internals.handler (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/handler.js:46:48)
    at exports.execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/handler.js:31:36)
    at Request._lifecycle (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/request.js:370:68)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async Request._execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/request.js:280:9) {
  generatedMessage: false,
  code: 'ERR_ASSERTION',
  actual: false,
  expected: true,
  operator: '==',
  isBoom: true,
  isServer: true,
  data: null,
  output: [Object]
}
curl localhost:3000/throw

1729274081766:something:416933:m2f13fpr:10001 Error: Hello World
    at handler (/home/stefan/[redacted]/src/index.ts:34:15)
    at exports.Manager.execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/toolkit.js:57:29)
    at internals.handler (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/handler.js:46:48)
    at exports.execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/handler.js:31:36)
    at Request._lifecycle (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/request.js:370:68)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async Request._execute (/home/stefan/[redacted]/node_modules/@hapi/hapi/lib/request.js:280:9) {
  isBoom: true,
  isServer: true,
  data: null,
  output: {
    statusCode: 500,
    payload: {
      statusCode: 500,
      error: 'Internal Server Error',
      message: 'An internal server error occurred'
    },
    headers: {}
  }
}

Metadata

Metadata

Assignees

Labels

bugBug or defect

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions