Skip to content

fix(core): recover embedded views whose first creation pass was incomplete#68172

Draft
arturovt wants to merge 1 commit intoangular:mainfrom
arturovt:fix/tnode_null
Draft

fix(core): recover embedded views whose first creation pass was incomplete#68172
arturovt wants to merge 1 commit intoangular:mainfrom
arturovt:fix/tnode_null

Conversation

@arturovt
Copy link
Copy Markdown
Contributor

When a directive throws during an embedded view's first creation pass, Angular marks the embedded TView as incompleteFirstPass and sets firstCreatePass to false. Any TNodes that hadn't been reached yet (because the template function exited early) remain null in the TView's data array.

On re-render (e.g. toggling an @if off then back on), the declaration functions — declareDirectiveHostTemplate and declareNoDirectiveHostTemplate — previously guarded TNode creation behind firstCreatePass only. With firstCreatePass=false and data[index]=null, they fell through to the else branch and passed null to templateCreate, which then crashed at tNode.flags |= flags:

  TypeError: Cannot read properties of null (reading 'flags')
  at templateCreate (chunk-S65TXBMT.js:3:72993)
  ...

This was observed in production via Rollbar and confirmed reproducible only in hydration mode (hydration enables the locateOrCreateContainerAnchor path that processes all template containers on creation, making the null slot reachable).

Fix: extend the guards in all three functions to also trigger when the relevant slot is uninitialized:

  • declareDirectiveHostTemplate / declareNoDirectiveHostTemplate: add || data[adjustedIndex] === null so the TNode is (re-)created when missing.
  • templateCreate: add || tNode.tView === null so the embedded TView is created for a freshly constructed TNode even when firstCreatePass is already false.

This mirrors the existing recovery logic in getOrCreateComponentTView, which already checks incompleteFirstPass before reusing a component TView.

…plete

When a directive throws during an embedded view's first creation pass,
Angular marks the embedded TView as `incompleteFirstPass` and sets
`firstCreatePass` to `false`. Any TNodes that hadn't been reached yet
(because the template function exited early) remain null in the TView's
data array.

On re-render (e.g. toggling an `@if` off then back on), the declaration
functions — `declareDirectiveHostTemplate` and `declareNoDirectiveHostTemplate` —
previously guarded TNode creation behind `firstCreatePass` only. With
`firstCreatePass=false` and `data[index]=null`, they fell through to the
else branch and passed `null` to `templateCreate`, which then crashed at
`tNode.flags |= flags`:

```
  TypeError: Cannot read properties of null (reading 'flags')
  at templateCreate (chunk-S65TXBMT.js:3:72993)
  ...
```

This was observed in production via Rollbar and confirmed reproducible
only in hydration mode (hydration enables the `locateOrCreateContainerAnchor`
path that processes all template containers on creation, making the null
slot reachable).

Fix: extend the guards in all three functions to also trigger when the
relevant slot is uninitialized:
- `declareDirectiveHostTemplate` / `declareNoDirectiveHostTemplate`: add
  `|| data[adjustedIndex] === null` so the TNode is (re-)created when
  missing.
- `templateCreate`: add `|| tNode.tView === null` so the embedded TView is
  created for a freshly constructed TNode even when firstCreatePass is
  already false.

This mirrors the existing recovery logic in `getOrCreateComponentTView`,
which already checks `incompleteFirstPass` before reusing a component TView.
@angular-robot angular-robot bot added the area: core Issues related to the framework runtime label Apr 13, 2026
@ngbot ngbot bot added this to the Backlog milestone Apr 13, 2026
@thePunderWoman
Copy link
Copy Markdown
Contributor

Woah, looks like you've opened a lot of issues/PRs recently. While we appreciate contributions from the community, triaging and reviewing a large influx of content in a short time period takes time away from other ongoing projects. As a result, we're closing these issues/PRs to maintain the team's focus.

Note that this is not necessarily a rejection of the goals or direction of any of these contributions in particular, so much as a reflection of the team's current capacity and priorities.

You are welcome to open a smaller subset of issues/PRs in accordance with our policy focused on the most important and impactful contributions and we will do our best to prioritize a response as soon as possible.

@arturovt arturovt deleted the fix/tnode_null branch April 13, 2026 16:53
@arturovt arturovt restored the fix/tnode_null branch April 13, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: core Issues related to the framework runtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants