Skip to content

Commit 925322f

Browse files
fix(observability): address PR review — make command:enqueued resilient and skip unserializable events [SDK-6016]
Review feedback on #1118: - command:enqueued (log) path called JSON.stringify directly, so a non-circular serialization failure (BigInt, throwing toJSON, Proxy trap) could still throw uncaught and abort the run. Route it through sanitizeForTask, which is try/catch-guarded. - sanitizeForTask's catch returned { serializationError } and forwarded it to cy.task, sending a malformed payload to the Node o11y handler. Return null as a "skip this event" sentinel instead; flush sites guard on it and drop the event rather than ship a malformed one. Net effect: graceful degradation is now total across all three sites — no crash, and no malformed event reaches the collector. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 28af664 commit 925322f

1 file changed

Lines changed: 18 additions & 5 deletions

File tree

bin/testObservability/cypress/index.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@ const getCircularReplacer = () => {
2525
};
2626
};
2727

28+
/*
29+
* Returns a decycled, JSON-safe plain object, or `null` if the payload still cannot be
30+
* serialized for a non-circular reason (BigInt, a throwing toJSON, a Proxy trap, etc.).
31+
* `null` is a "skip this event" sentinel — callers must NOT forward it to cy.task, because
32+
* the Node o11y handler expects a structured event payload, not an error stub. Skipping keeps
33+
* graceful degradation total: no crash, and no malformed event reaches the collector.
34+
*/
2835
const sanitizeForTask = (data) => {
2936
try {
3037
return JSON.parse(JSON.stringify(data, getCircularReplacer()));
3138
} catch (e) {
32-
/* Never let serialization of o11y data break the user's test run (graceful degradation). */
33-
return { serializationError: e && e.message ? e.message : String(e) };
39+
return null;
3440
}
3541
};
3642

@@ -236,7 +242,12 @@ Cypress.on('command:enqueued', (attrs) => {
236242
if (args.includes('test_observability_log') || args.includes('test_observability_command')) return;
237243
const message = args.reduce((result, logItem) => {
238244
if (typeof logItem === 'object') {
239-
return [result, JSON.stringify(logItem, getCircularReplacer())].join(' ');
245+
/* Route through sanitizeForTask so a non-circular serialization failure can never
246+
* throw out of the command:enqueued handler (same graceful-degradation contract as
247+
* the flush sites). sanitizeForTask returns a decycled plain object (safe to stringify)
248+
* or null; on null, contribute nothing for this item rather than crash. */
249+
const safeLog = sanitizeForTask(logItem);
250+
return [result, safeLog === null ? '' : JSON.stringify(safeLog)].join(' ');
240251
}
241252
return [result, logItem ? logItem.toString() : ''].join(' ');
242253
}, '');
@@ -337,7 +348,8 @@ beforeEach(() => {
337348

338349
if (eventsQueue.length > 0) {
339350
eventsQueue.forEach(event => {
340-
cy.task(event.task, sanitizeForTask(event.data), event.options);
351+
const payload = sanitizeForTask(event.data);
352+
if (payload !== null) cy.task(event.task, payload, event.options);
341353
});
342354
}
343355
eventsQueue = [];
@@ -352,7 +364,8 @@ afterEach(function() {
352364

353365
if (eventsQueue.length > 0) {
354366
eventsQueue.forEach(event => {
355-
cy.task(event.task, sanitizeForTask(event.data), event.options);
367+
const payload = sanitizeForTask(event.data);
368+
if (payload !== null) cy.task(event.task, payload, event.options);
356369
});
357370
}
358371

0 commit comments

Comments
 (0)