Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Error converting circular structure to JSON #14840

Open
benjaminjkraft opened this issue Jan 8, 2024 · 6 comments
Open

[Bug]: Error converting circular structure to JSON #14840

benjaminjkraft opened this issue Jan 8, 2024 · 6 comments

Comments

@benjaminjkraft
Copy link
Contributor

benjaminjkraft commented Jan 8, 2024

Version

v29.7.0, v30.0.0-alpha.2

Steps to reproduce

  1. Create a test file with the below test.
  2. npx jest --workerIdleMemoryLimit=1GB path/to/the.test.js (note: --workerIdleMemoryLimit is just to force Jest to use workers, which you could also trigger by running many tests, --watch, etc.)
  3. test suite crashes with only a circular reference error

Test:

it("tmp", () => {
	const e = new Error("yikes")
	e.e = e
	throw e
})

Expected behavior

I expect to see an error in my code! Which of course happens if you remove e.e = e.

Note that this works fine without workers, and in some cases it works fine even with them (I haven't figured out why.) It seems that in some code paths Jest effectively destructures the error, getting only the fields it will use (i.e. not e) and then passing those over to the parent process, but in others it serializes the whole error, which fails.

Actual behavior

The error is as follows:

 FAIL  ./tmp.test.js
  ● Test suite failed to run

    TypeError: Converting circular structure to JSON
        --> starting at object with constructor 'Error'
        --- property 'e' closes the circle
        at stringify (<anonymous>)

      at messageParent (../../.npm/_npx/fff1c0483a6bf0c3/node_modules/jest-worker/build/index.js:1572:19)

This gives very little information about what actually went wrong. (

Additional context

Of course in a simple repro the answer is simply "don't do that". But in the real cases I started from, I believe the error was likely thrown from third-party code, in an integration test in a large codebase so it's very hard to know. The circular structure in question was a ClientRequest, which could be used in many places. So having no stacktrace from Jest makes it very hard to track down.

For now I've hacked around it with the following patch:

diff --git a/node_modules/jest-worker/build/workers/messageParent.js b/node_modules/jest-worker/build/workers/messageParent.js
index 62e2cce..0083989 100644
--- a/node_modules/jest-worker/build/workers/messageParent.js
+++ b/node_modules/jest-worker/build/workers/messageParent.js
@@ -26,7 +26,28 @@ function messageParent(message, parentProcess = process) {
       message
     ]);
   } else if (typeof parentProcess.send === 'function') {
-    parentProcess.send([_types.PARENT_MESSAGE_CUSTOM, message]);
+    try {
+      parentProcess.send([_types.PARENT_MESSAGE_CUSTOM, message]);
+    } catch (e) {
+      if (!e.message.includes("circular structure to JSON")) {
+        throw e
+      }
+      // These errors are super annoying to debug; remove some information from
+      // the error to try to get it through to the parent process.
+      const { failureDetails } = message[1][1]
+      failureDetails.forEach((err, i) => {
+        // Keep the error message+stack, but drop other fields.
+        failureDetails[i] = new Error(e.message + "\nOriginal error was: " + err.message)
+        if (err.stack) {
+          failureDetails[i].stack = err.stack
+        }
+      })
+      try {
+        parentProcess.send([_types.PARENT_MESSAGE_CUSTOM, message]);
+      } catch (e2) {
+        throw e
+      }
+    }
   } else {
     throw new Error('"messageParent" can only be used inside a worker');
   }
diff --git a/node_modules/jest-worker/build/workers/processChild.js b/node_modules/jest-worker/build/workers/processChild.js
index 1a47f23..0100599 100644
--- a/node_modules/jest-worker/build/workers/processChild.js
+++ b/node_modules/jest-worker/build/workers/processChild.js
@@ -79,7 +79,30 @@ function reportSuccess(result) {
   if (!process || !process.send) {
     throw new Error('Child can only be used on a forked process');
   }
-  process.send([_types.PARENT_MESSAGE_OK, result]);
+  try {
+    process.send([_types.PARENT_MESSAGE_OK, result]);
+  } catch (e) {
+    if (!e.message.includes("circular structure to JSON")) {
+      throw e
+    }
+    result.testResults.forEach(testResult => {
+      // These errors are super annoying to debug; remove some information from
+      // the error to try to get it through to the parent process.
+      const { failureDetails } = testResult
+      failureDetails.forEach((err, i) => {
+        // Keep the error message+stack, but drop other fields.
+        failureDetails[i] = new Error(e.message + "\nOriginal error was: " + err.message)
+        if (err.stack) {
+          failureDetails[i].stack = err.stack
+        }
+      })
+    })
+    try {
+      process.send([_types.PARENT_MESSAGE_OK, result]);
+    } catch (e2) {
+      throw e
+    }
+  }
 }
 function reportClientError(error) {
   return reportError(error, _types.PARENT_MESSAGE_CLIENT_ERROR);

Probably the more robust approach is something more like what reportError does, but this was easier to hack in.

See also specific cases that may be the same/similar issues: #11958, #10577

Environment

System:
    OS: macOS 14.2.1
    CPU: (10) arm64 Apple M1 Max
  Binaries:
    Node: 18.17.0 - /nix/store/3pmw2p5lhkl6g572n1gc2la32x97815c-nodejs-18.17.0/bin/node
    npm: 9.6.7 - ~/.local/bin/npm
  npmPackages:
    jest: ^29.7.0 => 29.7.0
@benjaminjkraft benjaminjkraft changed the title [Bug]: [Bug]: Error converting circular structure to JSON Jan 8, 2024
Copy link

github-actions bot commented Feb 7, 2024

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

@github-actions github-actions bot added the Stale label Feb 7, 2024
@sualko
Copy link

sualko commented Feb 13, 2024

We see the same issue. Would be awesome if we could get a bug fix for this.

@github-actions github-actions bot removed the Stale label Feb 13, 2024
Copy link

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

@github-actions github-actions bot added the Stale label Mar 14, 2024
@benjaminjkraft
Copy link
Contributor Author

still relevant. not sure if/when I will have time to productionize this, so would encourage others who are affected to take a stab if you can!

@github-actions github-actions bot removed the Stale label Mar 15, 2024
Copy link

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

@github-actions github-actions bot added the Stale label Apr 14, 2024
@BigsonLvrocha
Copy link

still relevant

@github-actions github-actions bot removed the Stale label May 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants