Encountered "Converting circular structure to JSON error" when returning the customized BoomError response through server onPreResponse
- Dominant language
- JavaScript
- Stars
- 14.8k
- Forks
- 1.4k
- Avg merge
- 22d 3h
- Merged PRs (30d)
- 1
Description
#### Support plan
* *is this issue currently blocking your project?* (yes/no): no
* *is this issue affecting a production system?* (yes/no): yes
#### Context
* *node version*: 18.14.2
* *module version*:
* @hapi/hapi: 21.1.0
* joi: 17.8.3
* *environment* (e.g. node, browser, native): node
* *used with* (e.g. hapi application, another framework, standalone, ...): hapi application
* *any other relevant information*:
#### How can we help?
**Problem Description**
This error happened when we were using `joi` for schema validation through the validation function provided by hapi.
When we used the following way to throw an error when validation failed, it threw 500 internal server error when clients gave null value of `refId` twice or above (first time didn't have the 500 internal server error but 400 bad request which was expected).
```js
const schema = Joi.object({
refId: refId.required().error(new Error('refId is required field.')),
});
```
**Investigation**
This is our error plugin which handles Boom error
**index.js** (error plugin)
```js
const isTrue = require('../../modules/conditional-utils/isTrue');
const BoomError = require('./BoomError');
const register = async server => {
const { logger } = server.plugins;
server.ext('onPreResponse', (request, h) => {
const { response } = request;
// ...
// ...skip not important parts
if (isTrue(response.isBoom)) {
const error = new BoomError(response);
const { body, code, message } = error.render();
return h
.response(body)
.code(code)
.message(message);
}
return h.continue;
});
};
// ...
```
**BoomError.js**
`BoomError` model gets the 'data' from the response and then put it for `BaseError`
```js
// Boom: https://github.com/hapijs/boom
// HTTP-friendly error objects
const get = require('lodash/get');
const BaseError = require('./BaseError');
const defaultCode = 'BOOM_ERROR';
/**
* BoomError
*
* @class
* @extends BaseError
*/
module.exports = class BoomError extends BaseError {
/**
* Create an instance of BoomError
*
* @param {Object} object - the Boom object instance
* @memberof BoomError
*/
constructor(object) {
const message = get(object, ['output', 'payload', 'message']);
const name = get(object, ['output', 'payload', 'error']);
const status = get(object, ['output', 'payload', 'statusCode']);
const data = get(object, ['data']);
// Create the error using our Base Error
super(message, defaultCode, name, status, data);
}
};
```
**BaseError.js**
`BaseError` returns the data as a part of `body`
```js
// ...
// ...
module.exports = class BaseError extends ExtendableError {
/**
* Create an instance of BaseError
*
* @param {string} message - the error message
* @param {string} code - the error code
* @param {string} name - the error name
* @param {number} status - the http status code
* @param {*} [data] - the error object and/or additional data
* @memberof BaseError
*/
constructor(message, code, name, status, data) {
throwIfNotString(message);
throwIfNotString(code);
throwIfNotString(name);
throwIfNotNumber(status);
super(message);
this.code = code;
this.name = name;
this.status = status;
this.data = data;
// This field is used to allow internal services to determine
// whether the error object is an instance of Base Error (a bit like 'isBoom')
this.isBaseError = true;
}
// Render the error message for response to the user
render() {
const { status, name, code, message } = this;
const error = { status, name, code, message };
const data = isPresent(this.data) ? this.data : null;
return {
body: { error, data },
code: status,
message: name,
};
}
};
```
After doing some tracking, the error "Converting circular structure to JSON error" happened when executing `await internals.marshal(response);` in the transmit.js of hapi/lib as shown below.

Finally, I found it seems it's related to [#4229 failAction: detailedError to log, defaultError to response](https://github.com/hapijs/hapi/issues/4229) of the release 21.0.0
In the `validation.js` file, `data` field of `defaultError` will be different between the first and second triggering of joi validation.

First triggering, data was null


Second triggering, data was not null but we started getting 500 internal server error


Third triggering, data was not null but seems with circular structure (I guess that's why we got "Converting circular structure to JSON error")


I would like to know why this happened and if we can fix this issue on our side, or hapi will also deal with this issue?
Contributor guide
Assessment
This issue has not been assessed yet.