Skip to content

fix: infinite loop when object has an empty string key#58

Merged
RexSkz merged 1 commit intoRexSkz:mainfrom
trotzig:fix/empty-string-key-infinite-loop
Mar 3, 2026
Merged

fix: infinite loop when object has an empty string key#58
RexSkz merged 1 commit intoRexSkz:mainfrom
trotzig:fix/empty-string-key-infinite-loop

Conversation

@trotzig
Copy link
Copy Markdown
Contributor

@trotzig trotzig commented Mar 2, 2026

Problem

When diffing two objects where either side contains an empty string ("") as a key, diffObject enters an infinite loop and exhausts all available memory, crashing the process/browser tab.

The root cause is the key-advance logic at the end of the while loop:

if (!keyLeft) {
  keysRight.shift();
} else if (!keyRight) {
  keysLeft.shift();

Because !'' is true in JavaScript, when the current left key is the empty string the code thinks the left side is exhausted and only advances the right pointer. keysLeft is therefore never shifted past "", and once keysRight drains the loop spins forever.

Fix

Replace the falsy checks with strict === undefined comparisons:

if (keyLeft === undefined) {
  keysRight.shift();
} else if (keyRight === undefined) {
  keysLeft.shift();

Test

A regression test is added to differ.spec.ts that diffs two objects both containing an empty-string key and asserts: the call completes without throwing, both sides produce the same number of lines, and the changed value is marked as a modification.

Reproduction

const d = new Differ();
// Hangs / OOMs before this fix:
d.diff({ '': 'before', a: 1 }, { '': 'after', b: 2 });

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical bug in the object differ utility that led to infinite loops and application crashes when processing objects containing empty string keys. The fix refines the key comparison logic to accurately distinguish between an undefined key and an empty string key, thereby preventing erroneous loop conditions and enhancing the stability of the diffing process.

Highlights

  • Bug Fix: Infinite Loop: Resolved an issue in diffObject where diffing objects with empty string keys ("") would cause an infinite loop and memory exhaustion due to incorrect key-advance logic.
  • Logic Correction: Replaced JavaScript's falsy checks (!key) with strict === undefined comparisons for keyLeft and keyRight to correctly identify when a key is truly absent, rather than just an empty string.
  • Regression Test: Added a new test case in differ.spec.ts to ensure the fix prevents the infinite loop and correctly handles object diffs with empty string keys.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/differ.spec.ts
    • Added a regression test for the empty string key infinite loop scenario.
  • src/utils/diff-object.ts
    • Modified key-advance logic to use strict undefined checks instead of falsy checks.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request fixes an infinite loop that occurs when diffing objects with an empty string as a key. The fix correctly uses a strict undefined check instead of a falsy check to determine if the key arrays are exhausted. A regression test is added to cover this scenario. The implementation of the fix is correct. I've suggested a small improvement to the new test case to make it more concise and efficient by removing a redundant function call.

Comment thread src/differ.spec.ts
@trotzig
Copy link
Copy Markdown
Contributor Author

trotzig commented Mar 2, 2026

We found this bug at happo.io after seeing certain reports make the page unresponsive. I threw Claude at the data and it tracked it down to this issue in json-diff-kit.

Thank you very much for making this library available to the public @RexSkz! We've used it for a few years now and it's been great.

@RexSkz
Copy link
Copy Markdown
Owner

RexSkz commented Mar 3, 2026

Thank you for your contribution! It's really a rookie mistake in JavaScript.

BTW, do you think it's necessary to make code changes according to Gemini's advice? (I'm okay with it. If you are also okay, you can directly resolve the comment and merge this PR.)

The key-advance logic in `diffObject` used falsy checks (!keyLeft,
!keyRight) to detect when one side had run out of keys. Because an
empty string is falsy in JavaScript, any JSON object containing "" as
a key would cause the left pointer to never advance, producing an
infinite while-loop that exhausted all available memory.

Fix: replace the falsy checks with strict undefined comparisons
(=== undefined) so that empty-string keys are handled correctly.

A regression test is added to differ.spec.ts covering objects with
an empty-string key on both sides.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@trotzig trotzig force-pushed the fix/empty-string-key-infinite-loop branch from 1779cbf to e49db76 Compare March 3, 2026 08:39
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Mar 3, 2026

@trotzig
Copy link
Copy Markdown
Contributor Author

trotzig commented Mar 3, 2026

We have all made this mistake, don't worry!

I resolved the Gemini feedback without changes. I also signed the commits so that you can merge it.

@RexSkz RexSkz merged commit aad35a3 into RexSkz:main Mar 3, 2026
3 checks passed
@RexSkz
Copy link
Copy Markdown
Owner

RexSkz commented Mar 3, 2026

v1.0.35 has been published :)

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.

2 participants