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.

  1. Read the "starting at object with constructor" line: it names the object type that loops (Socket, IncomingMessage, a Mongoose or Sequelize model).
  2. Replace JSON.stringify(req) or res.json(model) with an object of the fields you need.
  3. For ORM records, call their toJSON() or toObject() or select plain rows.
  4. For error objects with a cause chain or a request attached, log message, code and stack, not the whole object.
  5. In logs, use util.inspect(value, { depth: 4 }) or a logger with safe serializers.
typescript
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 cycles

Why 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.

Plans from $29/moSee what your agent readsSee the setupCancel any time from the billing portal

Related errors

References: MDN: TypeError: cyclic object value, Node.js: util.inspect