Skip to content

fix: reset stale serialized state when JSON.stringify throws - #437

Open
hiro-nikaitou wants to merge 1 commit into
unjs:mainfrom
hiro-nikaitou:fix/stale-serialized-catch
Open

fix: reset stale serialized state when JSON.stringify throws#437
hiro-nikaitou wants to merge 1 commit into
unjs:mainfrom
hiro-nikaitou:fix/stale-serialized-catch

Conversation

@hiro-nikaitou

@hiro-nikaitou hiro-nikaitou commented Jul 13, 2026

Copy link
Copy Markdown

When logging objects with circular references, JSON.stringify inside the throttle block throws. The existing catch block silently absorbed the error without updating _lastLog.serialized, leaving it stale. A subsequent log could then be incorrectly compared against the old serialized value within the throttle window.

This fix resets _lastLog.serialized to undefined in the catch block, so the next comparison always starts fresh.

A test is added that verifies:

  1. A normal log sets _lastLog.serialized
  2. A circular-reference log triggers the catch block
  3. A subsequent different log is correctly emitted (not treated as a repeat of the first)

Summary by CodeRabbit

  • Bug Fixes

    • Fixed throttled logging so messages following an entry with circular data are displayed correctly instead of being incorrectly treated as duplicates.
  • Tests

    • Added coverage for logging behavior when circular references prevent serialization.

When logging objects with circular references, JSON.stringify inside the
throttle block throws. The catch block silently absorbed the error without
updating _lastLog.serialized, leaving it stale. A subsequent log could then
be incorrectly compared against the old serialized value.

Reset _lastLog.serialized to undefined in the catch block so the next log
always starts with a fresh comparison.
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The throttle serializer now clears _lastLog.serialized when JSON.stringify fails on circular data. A regression test verifies that subsequent distinct messages are not incorrectly treated as repeats.

Changes

Throttle serialization handling

Layer / File(s) Summary
Serialization reset and regression coverage
src/consola.ts, test/consola.test.ts
Circular-reference serialization failures reset cached state, and an asynchronous test verifies three distinct log entries, including the circular object.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main fix: resetting stale serialized state when JSON.stringify throws.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/consola.test.ts`:
- Around line 78-94: Make the regression test around consola.log explicitly fail
on the old implementation by verifying that the circular-reference log clears
consola’s _lastLog.serialized state, or change the final message to repeat
"Control message" with the required throttling setup so stale serialization
would suppress it. Keep the existing assertions for distinct output where they
remain applicable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 33340cef-083e-4782-b2d5-148830927c74

📥 Commits

Reviewing files that changed from the base of the PR and between c47faac and 0089b51.

📒 Files selected for processing (2)
  • src/consola.ts
  • test/consola.test.ts

Comment thread test/consola.test.ts
Comment on lines +78 to +94
// 1. Log a normal message - sets _lastLog.serialized
consola.log("Control message");

// 2. Log an object with circular reference - triggers catch block
const circular: { value: string; self?: any } = { value: "circular data" };
circular.self = circular;
consola.log(circular);

// 3. Log another normal message - should NOT be treated as repeat
consola.log("Different message");

await wait(300);

// All three messages should appear distinctly
expect(logs.length).toBe(3);
expect(logs[0].args[0]).toBe("Control message");
expect(logs[2].args[0]).toBe("Different message");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the regression test fail without the fix.

"Control message" and "Different message" serialize differently, so the stale "Control message" serialization cannot classify the final log as a repeat. This test therefore passes against the old implementation too. Assert _lastLog.serialized is cleared after the circular log, or repeat the original message with enough throttling state to trigger the old suppression behavior.

Suggested assertion
     consola.log(circular);
+    expect(consola._lastLog.serialized).toBeUndefined();
     consola.log("Different message");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 1. Log a normal message - sets _lastLog.serialized
consola.log("Control message");
// 2. Log an object with circular reference - triggers catch block
const circular: { value: string; self?: any } = { value: "circular data" };
circular.self = circular;
consola.log(circular);
// 3. Log another normal message - should NOT be treated as repeat
consola.log("Different message");
await wait(300);
// All three messages should appear distinctly
expect(logs.length).toBe(3);
expect(logs[0].args[0]).toBe("Control message");
expect(logs[2].args[0]).toBe("Different message");
// 1. Log a normal message - sets _lastLog.serialized
consola.log("Control message");
// 2. Log an object with circular reference - triggers catch block
const circular: { value: string; self?: any } = { value: "circular data" };
circular.self = circular;
consola.log(circular);
expect(consola._lastLog.serialized).toBeUndefined();
// 3. Log another normal message - should NOT be treated as repeat
consola.log("Different message");
await wait(300);
// All three messages should appear distinctly
expect(logs.length).toBe(3);
expect(logs[0].args[0]).toBe("Control message");
expect(logs[2].args[0]).toBe("Different message");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/consola.test.ts` around lines 78 - 94, Make the regression test around
consola.log explicitly fail on the old implementation by verifying that the
circular-reference log clears consola’s _lastLog.serialized state, or change the
final message to repeat "Control message" with the required throttling setup so
stale serialization would suppress it. Keep the existing assertions for distinct
output where they remain applicable.

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