Skip to content

I added a test that asserts malformed profiling operations do not throw and that errors/warnings are logged#502

Open
everettbu wants to merge 6 commits into
mainfrom
main-35674
Open

I added a test that asserts malformed profiling operations do not throw and that errors/warnings are logged#502
everettbu wants to merge 6 commits into
mainfrom
main-35674

Conversation

@everettbu

Copy link
Copy Markdown

Mirror of facebook/react#35674
Original author: apoorvgarewal07


  1. Builds minimal fake profiling data with a valid initial snapshot.
  2. Injects malformed operations (duplicate add, remove of missing node, remove root, and an unsupported op).
  3. Asserts that getCommitTree does not throw and that console.error / console.warn were called.

@greptile-apps

greptile-apps Bot commented Feb 11, 2026

Copy link
Copy Markdown

Greptile Summary

This PR makes CommitTreeBuilder.updateTree() more resilient by replacing throw with console.error/console.warn for malformed profiling operations (duplicate add, missing node on remove, unsupported ops), and adds a new test to verify the non-throwing behavior.

  • TREE_OPERATION_REMOVE_ROOT case references an undefined constant — the constant was previously removed from constants.js and is not imported, making this new case unreachable dead code.
  • Duplicate-add early break corrupts operation parsing — when a duplicate add is detected, the parser index i is not advanced past the remaining fields of the ADD operation payload, causing all subsequent operations to be read from incorrect positions.
  • The test exercises some malformed scenarios but its ops1 array is not well-aligned to the actual operation encoding, so assertions pass incidentally rather than validating each specific error path.

Confidence Score: 1/5

  • This PR has bugs that would cause incorrect runtime behavior — the TREE_OPERATION_REMOVE_ROOT case is dead code and the duplicate-add handler corrupts parser state.
  • Score of 1 reflects two concrete bugs in production code: an undefined constant making a case branch unreachable, and a parser index misalignment that would cause cascading misinterpretation of subsequent operations whenever a duplicate add is encountered.
  • Pay close attention to packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js — both the duplicate-add handler and the REMOVE_ROOT case need fixes.

Important Files Changed

Filename Overview
packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js Converts throws to console.error/warn for resilience, but has two bugs: TREE_OPERATION_REMOVE_ROOT is undefined (dead case), and duplicate-add break doesn't advance the parser index past remaining fields, corrupting subsequent operation parsing.
packages/react-devtools-shared/src/tests/profilingCommitTreeBuilder-test.js New test for malformed operations. The ops array is not properly aligned to the operation encoding format (as noted in previous threads), and the test doesn't exercise TREE_OPERATION_REMOVE_ROOT since that constant is undefined. Assertions pass incidentally rather than validating each error path.

Fix All in Claude Code Fix All in Codex

Last reviewed commit: 5f8690e

@greptile-apps greptile-apps 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.

2 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +337 to +339

it('should tolerate malformed operations without throwing', () => {
const {getCommitTree, invalidateCommitTrees} = require('react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Test doesn’t hit all cases

This test claims to inject “duplicate add” and “remove root”, but ops1 only includes an ADD for id=2 once, and uses op code 6 (which is TREE_OPERATION_SET_SUBTREE_MODE), not TREE_OPERATION_REMOVE_ROOT. As written, it doesn’t actually verify the new non-throwing behavior for those scenarios.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder-test.js
Line: 337:339

Comment:
**Test doesn’t hit all cases**

This test claims to inject “duplicate add” and “remove root”, but `ops1` only includes an ADD for id=2 once, and uses op code `6` (which is `TREE_OPERATION_SET_SUBTREE_MODE`), not `TREE_OPERATION_REMOVE_ROOT`. As written, it doesn’t actually verify the new non-throwing behavior for those scenarios.

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps

greptile-apps Bot commented Feb 11, 2026

Copy link
Copy Markdown
Additional Comments (2)

packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Cloning missing-node breaks

getClonedNode no longer guards against nodes.get(id) being null/undefined. For malformed ops (e.g. ADD with a missing parent, REORDER on a missing id, UPDATE_TREE_BASE_DURATION on a missing id), this now does Object.assign({}, undefined) and returns a “node” without required fields. The next access (e.g. parentNode.children.concat(...)) will throw, defeating the goal of tolerating malformed operations. Consider restoring the previous null check (and logging/ignoring the op) rather than cloning an absent node.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Line: 182:184

Comment:
**Cloning missing-node breaks**

`getClonedNode` no longer guards against `nodes.get(id)` being `null/undefined`. For malformed ops (e.g. ADD with a missing parent, REORDER on a missing id, UPDATE_TREE_BASE_DURATION on a missing id), this now does `Object.assign({}, undefined)` and returns a “node” without required fields. The next access (e.g. `parentNode.children.concat(...)`) will throw, defeating the goal of tolerating malformed operations. Consider restoring the previous null check (and logging/ignoring the op) rather than cloning an absent node.

How can I resolve this? If you propose a fix, please make it concise.

packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Duplicate ADD desyncs parser

On duplicate TREE_OPERATION_ADD, the code breaks without advancing i to skip the remaining fields for that ADD op. That leaves i pointing at the add payload (parentID/ownerID/name/key/etc), and the loop will interpret those values as new operations, which can cause incorrect parsing and downstream errors. To safely ignore duplicate adds, i still needs to be incremented by the correct payload length for root vs non-root adds.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Line: 214:216

Comment:
**Duplicate ADD desyncs parser**

On duplicate `TREE_OPERATION_ADD`, the code `break`s without advancing `i` to skip the remaining fields for that ADD op. That leaves `i` pointing at the add payload (parentID/ownerID/name/key/etc), and the loop will interpret those values as new operations, which can cause incorrect parsing and downstream errors. To safely ignore duplicate adds, `i` still needs to be incremented by the correct payload length for root vs non-root adds.

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps greptile-apps 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.

2 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +264 to +302
it('should tolerate malformed operations without throwing', () => {
const {getCommitTree, invalidateCommitTrees} = require('react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder');
const {
ElementTypeFunction,
ElementTypeRoot,
} = require('react-devtools-shared/src/frontend/types');

const rootID = 1;
const snapshots = new Map();
snapshots.set(rootID, {
id: rootID,
children: [2],
displayName: 'Root',
hocDisplayNames: null,
key: null,
type: ElementTypeRoot,
compiledWithForget: false,
});
snapshots.set(2, {
id: 2,
children: [],
displayName: 'Child',
hocDisplayNames: null,
key: null,
type: ElementTypeFunction,
compiledWithForget: false,
});
const initialTreeBaseDurations = new Map();
initialTreeBaseDurations.set(rootID, 0);
initialTreeBaseDurations.set(2, 0);

const ops0 = [0, rootID, 0];
const ops1 = [
0, rootID, 0,
1, 2, ElementTypeFunction, rootID, 0, 0, 0,
2, 1, 999,
6,
99,
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Malformed ops array

ops1 starts with [0, rootID, 0], so updateTree() will treat rootID as the commit tree root and then 0 as stringTableSize; the next value (1) becomes the first operation. As constructed, the sequence 1, 2, ElementTypeFunction, rootID, 0, 0, 0, ... is not aligned to the TREE_OPERATION_ADD encoding expected by CommitTreeBuilder.updateTree and will cause the parser to read incorrect fields (e.g. parentID/ownerID/string IDs) and advance i incorrectly (packages/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder-test.js:295-302). This makes the test assertions unreliable.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder-test.js
Line: 264:302

Comment:
**Malformed ops array**

`ops1` starts with `[0, rootID, 0]`, so `updateTree()` will treat `rootID` as the commit tree root and then `0` as `stringTableSize`; the next value (`1`) becomes the first operation. As constructed, the sequence `1, 2, ElementTypeFunction, rootID, 0, 0, 0, ...` is not aligned to the `TREE_OPERATION_ADD` encoding expected by `CommitTreeBuilder.updateTree` and will cause the parser to read incorrect fields (e.g. parentID/ownerID/string IDs) and advance `i` incorrectly (`packages/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder-test.js:295-302`). This makes the test assertions unreliable.

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps

greptile-apps Bot commented Feb 11, 2026

Copy link
Copy Markdown
Additional Comments (2)

packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Undefined constant import

CommitTreeBuilder.js imports TREE_OPERATION_REMOVE_ROOT (packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:10-24), but packages/react-devtools-shared/src/constants.js explicitly notes it was removed and does not export it. This will make the module fail to load at runtime (and the new switch case can never work).

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Line: 10:24

Comment:
**Undefined constant import**

`CommitTreeBuilder.js` imports `TREE_OPERATION_REMOVE_ROOT` (`packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:10-24`), but `packages/react-devtools-shared/src/constants.js` explicitly notes it was removed and does not export it. This will make the module fail to load at runtime (and the new switch case can never work).

How can I resolve this? If you propose a fix, please make it concise.

packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Incorrect op parsing

In the TREE_OPERATION_SET_SUBTREE_MODE case, mode is read from operations[i + 1] (same as id), and then i += 3 even though only two operands appear to be consumed (packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:345-353). This will desynchronize the parser and corrupt processing of subsequent operations.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Line: 345:353

Comment:
**Incorrect op parsing**

In the `TREE_OPERATION_SET_SUBTREE_MODE` case, `mode` is read from `operations[i + 1]` (same as `id`), and then `i += 3` even though only two operands appear to be consumed (`packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:345-353`). This will desynchronize the parser and corrupt processing of subsequent operations.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +320 to +323
case TREE_OPERATION_REMOVE_ROOT: {
console.warn('Operation REMOVE_ROOT is not supported while profiling. Ignoring.');
break;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

TREE_OPERATION_REMOVE_ROOT is undefined — this case is dead code

TREE_OPERATION_REMOVE_ROOT was removed from constants.js (line 25: // Removed TREE_OPERATION_REMOVE_ROOT) and is not imported in this file. At runtime, this case matches against undefined, which will never equal any numeric operation code. This means the handler is unreachable dead code, and any actual operation code 6 (the old value of TREE_OPERATION_REMOVE_ROOT) will fall through to the default branch and halt parsing instead.

Either import or define a constant for the old value (e.g. const TREE_OPERATION_REMOVE_ROOT = 6;) or remove this case entirely.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Line: 320-323

Comment:
**`TREE_OPERATION_REMOVE_ROOT` is undefined — this case is dead code**

`TREE_OPERATION_REMOVE_ROOT` was removed from `constants.js` (line 25: `// Removed TREE_OPERATION_REMOVE_ROOT`) and is not imported in this file. At runtime, this `case` matches against `undefined`, which will never equal any numeric operation code. This means the handler is unreachable dead code, and any actual operation code `6` (the old value of `TREE_OPERATION_REMOVE_ROOT`) will fall through to the `default` branch and halt parsing instead.

Either import or define a constant for the old value (e.g. `const TREE_OPERATION_REMOVE_ROOT = 6;`) or remove this case entirely.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

Comment on lines 204 to 210
if (nodes.has(id)) {
throw new Error(
`Commit tree already contains fiber "${id}". This is a bug in React DevTools.`,
// Duplicate add detected; log and skip instead of throwing to avoid crashing the UI.
console.error(
`Commit tree already contains fiber "${id}". Skipping duplicate add.`,
);
break;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicate-add break corrupts the operation parser index

When a duplicate add is detected, the code breaks out of the switch after only advancing i by 3 (past opcode, id, type). However, a non-root TREE_OPERATION_ADD consumes 5 additional fields (parentID, ownerID, displayNameStringID, keyStringID, namePropStringID), and a root-type add consumes 4 additional fields. By breaking early without advancing i past these fields, the next loop iteration reads from the middle of the current operation's payload, misinterpreting data values as operation codes. This will cause cascading parse errors for the remainder of the operations array.

The fix should skip the correct number of remaining fields based on type before breaking, for example:

Suggested change
if (nodes.has(id)) {
throw new Error(
`Commit tree already contains fiber "${id}". This is a bug in React DevTools.`,
// Duplicate add detected; log and skip instead of throwing to avoid crashing the UI.
console.error(
`Commit tree already contains fiber "${id}". Skipping duplicate add.`,
);
break;
}
if (nodes.has(id)) {
// Duplicate add detected; log and skip instead of throwing to avoid crashing the UI.
console.error(
`Commit tree already contains fiber "${id}". Skipping duplicate add.`,
);
if (type === ElementTypeRoot) {
i += 4; // isStrictModeCompliant, Profiling flag, supportsStrictMode, hasOwnerMetadata
} else {
i += 5; // parentID, ownerID, displayNameStringID, keyStringID, nameProp
}
break;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js
Line: 204-210

Comment:
**Duplicate-add `break` corrupts the operation parser index**

When a duplicate add is detected, the code `break`s out of the switch after only advancing `i` by 3 (past opcode, id, type). However, a non-root `TREE_OPERATION_ADD` consumes 5 additional fields (parentID, ownerID, displayNameStringID, keyStringID, namePropStringID), and a root-type add consumes 4 additional fields. By breaking early without advancing `i` past these fields, the next loop iteration reads from the middle of the current operation's payload, misinterpreting data values as operation codes. This will cause cascading parse errors for the remainder of the operations array.

The fix should skip the correct number of remaining fields based on `type` before breaking, for example:

```suggestion
        if (nodes.has(id)) {
          // Duplicate add detected; log and skip instead of throwing to avoid crashing the UI.
          console.error(
            `Commit tree already contains fiber "${id}". Skipping duplicate add.`,
          );
          if (type === ElementTypeRoot) {
            i += 4; // isStrictModeCompliant, Profiling flag, supportsStrictMode, hasOwnerMetadata
          } else {
            i += 5; // parentID, ownerID, displayNameStringID, keyStringID, nameProp
          }
          break;
        }
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants