Skip to content

assert: fix TypeError on deepStrictEqual with null Map key or Set member - #64449

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
semx:fix-deepstrictequal-map-null-key
Sep 12, 2026
Merged

assert: fix TypeError on deepStrictEqual with null Map key or Set member#64449
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
semx:fix-deepstrictequal-map-null-key

Conversation

@semx

@semx semx commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

assert.deepStrictEqual() (and util.isDeepStrictEqual()) throw a TypeError instead of comparing when the first Map has a null (or other primitive) key that lines up against object-only keys in the other map:

const assert = require('node:assert');

const a = new Map([[null, 1], [{}, 2]]);
const b = new Map([[{}, 9], [{}, 9]]);

assert.deepStrictEqual(a, b);
// TypeError: Cannot read properties of null (reading 'constructor')
// expected: an AssertionError (the maps are not deeply equal)

The same happens for an undefined key. Set has the identical problem for a null/undefined member (once the set is large enough to skip the small-set fast path):

assert.deepStrictEqual(new Set([null, {}, {}]), new Set([{}, {}, {}]));
// TypeError: Cannot read properties of null (reading 'constructor')

It only triggers in strict mode when the other collection's keys/members are all objects and their count equals the first collection's size.

Cause

In mapObjectEquiv and setObjectEquiv (lib/internal/util/comparisons.js), primitive/null keys and members are resolved directly via b.has() / b.get(), but that handling was gated behind extraChecks (array.length !== a.size). When the counts match, the gate is skipped and the primitive/null key/member falls through to objectComparisonStart, which dereferences .constructor and throws on null/undefined.

Fix

Handle primitive/null keys and members unconditionally — they can only match by identity and can never match through the object comparator — so they are always resolved by direct lookup and never reach objectComparisonStart. The collections above now compare as unequal (throwing an AssertionError, as expected) instead of throwing a TypeError. Object comparison is unchanged.

Added regression cases (null and undefined keys/members, for both Map and Set) to test/parallel/test-assert-deep.js.

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. util Issues and PRs related to the built-in util module. labels Jul 12, 2026
@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.17%. Comparing base (c909c63) to head (5a3b354).
⚠️ Report is 32 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #64449   +/-   ##
=======================================
  Coverage   90.17%   90.17%           
=======================================
  Files         771      771           
  Lines      265451   265453    +2     
  Branches    50459    50465    +6     
=======================================
+ Hits       239361   239365    +4     
+ Misses      17059    17047   -12     
- Partials     9031     9041   +10     
Files with missing lines Coverage Δ
lib/internal/util/comparisons.js 99.53% <100.00%> (+<0.01%) ⬆️

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@semx
semx force-pushed the fix-deepstrictequal-map-null-key branch from 19f16d9 to 98843e4 Compare July 15, 2026 22:00
@semx

semx commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

This has two approvals (thanks @ljharb, @jasnell) and is still labelled needs-ci. Could a collaborator kick off a CI run?

Happy to rebase first if that helps — the branch is otherwise unchanged since the reviews.

@semx

semx commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Following up: this has two approvals (thanks @ljharb, @jasnell) and is only waiting on CI to run so it can land. Could a collaborator start CI / apply the label so it can move through the commit-queue? Happy to rebase if needed. Thanks!

@ljharb

ljharb commented Aug 6, 2026

Copy link
Copy Markdown
Member

First I think it needs a rebase?

@semx
semx force-pushed the fix-deepstrictequal-map-null-key branch from 98843e4 to cdd7206 Compare August 6, 2026 10:03
@semx

semx commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and force-pushed. There was one conflict, in lib/internal/util/comparisons.js: main has since landed an equivalent fix for the Map path (mapObjectEquiv), so I dropped that now-redundant hunk and kept the still-needed Set-path fix (setObjectEquiv) plus all four null/undefined regression tests. The final diff is just the Set fix + tests; the branch is one commit on top of main and both files pass node --check. Should be good for CI now — thanks!

@semx
semx force-pushed the fix-deepstrictequal-map-null-key branch from cdd7206 to d28dd4e Compare September 7, 2026 12:00
@semx

semx commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main — conflict-free (the earlier Map-path hunk stays dropped since mapObjectEquiv landed; this is just the Set-path fix plus the null/undefined regression tests). The PR is mergeable and only needs-ci; the fresh workflow runs are sitting in action_required, so they need a collaborator to approve the run for this fork PR. If someone could approve the Actions run and start CI, it should be ready to move through the commit-queue. Thanks again @ljharb @jasnell for the reviews.

@MikeMcC399

This comment was marked as outdated.

@MikeMcC399

This comment was marked as resolved.

deepStrictEqual() and util.isDeepStrictEqual() threw "Cannot read
properties of null (reading 'constructor')" instead of comparing when
a Map key or Set member was null/undefined (or another primitive) and
lined up against object-only keys/members in the other collection with
an equal count. The primitive/null handling was gated behind an
optimization that is skipped when the counts match, letting such keys
reach objectComparisonStart, which dereferences `.constructor`.

Resolve primitive and null keys/members directly in every case.

Signed-off-by: semx <7532921+semx@users.noreply.github.com>
@semx
semx force-pushed the fix-deepstrictequal-map-null-key branch from d28dd4e to 5a3b354 Compare September 11, 2026 11:00
@semx

semx commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main, thanks. Ready for a CI run whenever a collaborator has a minute.

@MikeMcC399 MikeMcC399 added author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. labels Sep 11, 2026
@MikeMcC399

Copy link
Copy Markdown
Contributor

Rebased onto current main, thanks. Ready for a CI run whenever a collaborator has a minute.

Many thanks for rebasing! That was successful in GitHub Actions CI.

Note that the needs-ci PRs that need a full CI run. label is a little misleading.

The needs-ci label identifies pull requests that require a full Jenkins CI run. It is a classification, not an indication that CI is still pending.

In any case I've now requested a Jenkins CI run, so we'll see what happens!

@MikeMcC399

This comment was marked as resolved.

@MikeMcC399 MikeMcC399 removed the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Sep 11, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Sep 11, 2026
@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@MikeMcC399

This comment was marked as outdated.

@MikeMcC399 MikeMcC399 added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Sep 12, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@MikeMcC399

Copy link
Copy Markdown
Contributor

CI was finally successful! 🎉

@panva panva added the commit-queue PRs queued for automated landing through the Commit Queue. label Sep 12, 2026
@nodejs-github-bot
nodejs-github-bot merged commit 6b28d88 into nodejs:main Sep 12, 2026
80 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in 6b28d88

@nodejs-github-bot nodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. needs-ci PRs that need a full CI run. util Issues and PRs related to the built-in util module.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants