Skip to content

fix(observability): prevent "Converting circular structure to JSON" crash from circular cy.task payloads [SDK-6016]#1118

Open
harshit-browserstack wants to merge 1 commit into
masterfrom
fix/sdk-6016-o11y-circular-args
Open

fix(observability): prevent "Converting circular structure to JSON" crash from circular cy.task payloads [SDK-6016]#1118
harshit-browserstack wants to merge 1 commit into
masterfrom
fix/sdk-6016-o11y-circular-args

Conversation

@harshit-browserstack
Copy link
Copy Markdown
Collaborator

@harshit-browserstack harshit-browserstack commented Jun 6, 2026

Problem

SDK-6016 When BrowserStack Test Observability is enabled, the Cypress instrumentation captures command args (command.attributes.args) and cy.log items raw and ships them to the Node plugin via cy.task(...), which JSON-serializes the payload (browser → Node). If a command/log arg is a circular Cypress runtime object — e.g. one whose renderOptions.host references itself — serialization throws:

Converting circular structure to JSON
    --> starting at object with constructor 't'
    |     property 'renderOptions' -> object with constructor 'Object'
    --- property 'host' closes the circle

…and aborts the entire test run. There was no try/catch around the flush, so an o11y serialization failure breaks the user's tests — contrary to the graceful-degradation contract.

This commonly surfaces in component-testing setups (@cypress/vite-dev-server / @cypress/vue) where mount/dev-server config objects are self-referential; e2e args are usually primitive and never trip it, which is why it stayed latent.

Fix

  • Add a WeakSet-based getCircularReplacer() and a sanitizeForTask() that decycles the payload (circular refs → "[Circular]") before it is handed to cy.task.
  • Apply it at both flush points (beforeEach/afterEach) and at the command:enqueued log stringify.
  • sanitizeForTask wraps serialization in a try/catch, so o11y instrumentation can never break the user's test run.

Non-circular fields are preserved; only true cycles are replaced.

Verification

  • Local (byte-faithful): a self-referential renderOptions.host object throws the exact error under raw JSON.stringify; with sanitizeForTask it ships cleanly.
  • Real BrowserStack build: a spec issuing a circular command arg (cfg.renderOptions.host = cfg) with testObservability: true completed 1/1 passed with no "Converting circular structure" in logs — build 0e384b2ee0de66beb4fe0254a85d1c66e937e02b.

Scope

Fix-only — single file (bin/testObservability/cypress/index.js), no version bump, no CHANGELOG (owned by the release PR).

🤖 Generated with Claude Code

…Converting circular structure to JSON" crash [SDK-6016]

The Cypress Observability instrumentation captures command args
(command.attributes.args) and cy.log items raw and ships them to the Node
plugin via cy.task(), which JSON-serializes the payload. When a command/log
arg is a circular Cypress runtime object (e.g. one whose renderOptions.host
references itself), serialization throws "Converting circular structure to
JSON" and aborts the entire test run.

Add a WeakSet-based getCircularReplacer() and a sanitizeForTask() that
decycles the payload (circular refs -> "[Circular]") before handing it to
cy.task, applied at both flush points (beforeEach/afterEach) and the
command:enqueued log stringify. sanitizeForTask wraps serialization in a
try/catch so o11y instrumentation can never break the user's test run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant