Guard default card/field templates against a transiently undefined model - #5681
Merged
lukemelia merged 2 commits intoAug 4, 2026
Conversation
The default isolated/edit and field-edit templates derived the card/field
class from `this.args.model.constructor` inside tracked getters, and
dereferenced `this.args.fields` unguarded. The host can invoke a format
component for a tick while the model instance is still resolving — on
initial load, and on a store re-resolve when an incremental index
invalidation lands under an open card — so the first tracked getter throws
`Cannot read properties of undefined (reading 'constructor')` and takes
down the render. Templates are null-safe on undefined paths; JS getters
are not.
Read the field definitions off the card/field class instead, which the
render path already threads in as `@cardOrField` (a stable value
independent of the model): `getBoxComponent` receives it and
`field-component.gts` passes it to every `CardOrFieldFormatComponent`, and
`BaseDefComponent` already declares it. `getField` accepts a class
directly, so this is a drop-in for `model.constructor`.
Also guard the `@fields`-dependent paths: `displayFields` returns early
when `@fields` is absent, `isThemeCard` coalesces to `{}`, the CardInfo
edit header renders only when `@fields.cardInfo` exists, and the notes
footer is wrapped in `{{#if @fields.cardInfo.notes}}` (invoking
`<@fields.cardInfo.notes />` with undefined `@fields` throws "attempted to
invoke undefined component"). Behavior is unchanged when the model and
fields are present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Host Test Results 1 files 1 suites 2h 53m 11s ⏱️ Results for commit 7f3c78f. |
Contributor
There was a problem hiding this comment.
Pull request overview
Updates the base realm’s default card and field edit templates to be resilient when the host briefly renders them before the backing model/fields are fully available, avoiding crashes from JS-side dereferences.
Changes:
- Use the stable
@cardOrFieldclass arg forgetField(...)lookups instead of@model.constructor. - Add guards/conditional rendering around
@fields-dependent paths (displayFields, theme detection, cardInfo edit header, notes footer). - Thread
cardOrFieldinto the affected template signatures to support the above change.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/base/default-templates/isolated-and-edit.gts | Switch field-definition reads to @cardOrField, and guard @fields accesses/conditional subrenders to prevent transient-resolution crashes. |
| packages/base/default-templates/field-edit.gts | Switch field-icon lookup to use @cardOrField instead of @model.constructor to avoid dereferencing an undefined model. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The isolated-format card-info header passed `@icon={{@model.constructor.icon}}`.
When the model is transiently undefined, that resolves to an undefined icon,
and `card-info.gts` renders `<@ICON />` unguarded when there is no thumbnail —
crashing the render, the same failure mode this change set guards elsewhere.
Read the icon off `@cardOrField` (the stable class arg already threaded for the
field-definition lookups) so it stays defined while the model resolves.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lukemelia
marked this pull request as ready for review
August 4, 2026 16:57
backspace
approved these changes
Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The production host intermittently crashes while rendering a card:
The default isolated/edit and field-edit templates derive the card/field class from
this.args.model.constructorinside tracked getters, and dereferencethis.args.fieldsunguarded. The host can invoke a format component for a tick while the model instance is still resolving — on initial load, and on a store re-resolve when an incremental index invalidation lands under an open card — so the first tracked getter throws and takes down the render. Glimmer templates are null-safe on undefined paths; JS getters are not.Fix
Read the field definitions off the card/field class instead of the model instance. The render path already threads the class in as
@cardOrField— a stable value independent of the model:getBoxComponentreceives it,field-component.gtspasses it to everyCardOrFieldFormatComponent, andBaseDefComponentalready declares it in itsArgs.getField()accepts a class directly, sogetField(this.args.cardOrField, …)is a drop-in forgetField(this.args.model.constructor, …)that never touches the transient model.The
@fields-dependent paths are guarded too:displayFieldsreturns early when@fieldsis absentisThemeCardcoalesces to{}@fields.cardInfoexists{{#if @fields.cardInfo.notes}}— invoking<@fields.cardInfo.notes />with undefined@fieldsthrows "attempted to invoke undefined component"Behavior is unchanged when the model and fields are present.
This supersedes the earlier #5608, which was closed because it was authored alongside unrelated userland work; this reapplies the
@cardOrFieldapproach cleanly on currentmain.Notes
atom.gtshas the samemodel.constructorshape but already guards with an earlyif (!this.args.model) return;, so it is not vulnerable and is left unchanged.ember-template-lint(clean) and hostlint:types(no errors); behavior-preserving for the normal load path.🤖 Generated with Claude Code