TypeError: Converting circular structure to JSON
Node.js · updated 2026-11-03
Thrown by JSON.stringify, res.json() and loggers that serialize objects, when the object graph contains a cycle. Node prints the path of the loop ("starting at object with constructor ... closes the circle"), which names the objects involved.
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Socket'
| property 'parser' -> object with constructor 'HTTPParser'
--- property 'socket' closes the circle
at JSON.stringify (<anonymous>)
at logRequest (file:///app/src/log.js:7:22)Quick fix
Do not serialize whole framework objects. Pick the fields you need (req.method, req.url, a model's toJSON() or plain fields) and stringify that. When you must log an unknown object, use util.inspect, which handles cycles, instead of JSON.stringify.
- Read the "starting at object with constructor" line: it names the object type that loops (Socket, IncomingMessage, a Mongoose or Sequelize model).
- Replace JSON.stringify(req) or res.json(model) with an object of the fields you need.
- For ORM records, call their toJSON() or toObject() or select plain rows.
- For error objects with a cause chain or a request attached, log message, code and stack, not the whole object.
- In logs, use util.inspect(value, { depth: 4 }) or a logger with safe serializers.
import { inspect } from 'node:util';
// Before: res.json(req) or JSON.stringify(err) with a socket inside
function requestSummary(req: import('node:http').IncomingMessage) {
return { method: req.method, url: req.url, ua: req.headers['user-agent'] };
}
logger.info(JSON.stringify(requestSummary(req)));
logger.debug(inspect(unknownValue, { depth: 4 })); // safe with cyclesWhy it happens
Logging or returning a request or response object
req and res link to the socket, which links back to them. JSON.stringify(req) or res.json({ req }) always hits this cycle.
ORM model instances
Mongoose documents, Sequelize instances and similar objects hold references to their connection and parent models. Return plain objects instead.
Errors with attached context
HTTP client errors (axios, got) carry the request and response, which contain sockets. Serializing the whole error to send it somewhere hits the cycle.
Your own data with parent links
Trees where children point at their parent, or caches that reference themselves, form a cycle that JSON cannot represent.
Other fixes
- Add a toJSON() method to your own classes that returns just the data fields.
- For trees, store parent ids instead of parent objects in anything you serialize.
Same error, other wording
- TypeError: Converting circular structure to JSON --> starting at object with constructor 'ClientRequest'
- TypeError: Converting circular structure to JSON --> starting at object with constructor 'MongoClient'
- TypeError: cyclic object value
Let your coding agent catch the next one
Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:
> Find the 'Converting circular structure to JSON' errors in production, show me which object each tried to serialize (from the constructor line) and where, and replace those with explicit plain objects.
Related errors
- SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
- TypeError: Cannot read properties of undefined (reading 'x')
- Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
References: MDN: TypeError: cyclic object value, Node.js: util.inspect