Overview
Discovered while working on #3510896: Add a new internal HTTP API for candidate `DynamicPropSource`s to enable a `ContentTemplate` UI.
Proposed resolution
User interface changes
Part one: Shape matching (MR !242)
- Before
- (same for videos!)
- After

Part two: Component source (MR !319)
Fixing the above made many commonly optional fields for the first time available in the ContentTemplate UI. Consequently, it unveiled another bug: see #9.
Basically: the SDC subsystem really doesn't like being given "text" => NULL for a type: string SDC prop called text that is optional — see core issue #3531905: Validation error on optional properties..
Fortunately, within Canvas, we can ensure we never do that. 👍
- Before (#9)
Twig\Error\RuntimeError occurred during rendering of component d9904f4d-3b6d-43c2-8312-8360f26299d7 in Content template Article content items — Full content view (node.article.full): An exception has been thrown during the rendering of a template ("[canvas_test_sdc:image-optional-with-example-and-additional-prop/image.src] NULL value found, but a string is required. This may be because the property is empty instead of having data present. If possible fix the source data, use the |default() twig filter, or update the schema to allow multiple types.. [canvas_test_sdc:image-optional-with-example-and-additional-prop/image.alt] NULL value found, but a string is required. This may be because the property is empty instead of having data present. If possible fix the source data, use the |default() twig filter, or update the schema to allow multiple types.. [canvas_test_sdc:image-optional-with-example-and-additional-prop/image.width] NULL value found, but an integer is required. [canvas_test_sdc:image-optional-with-example-and-additional-prop/image.height] NULL value found, but an integer is required.") in "canvas_test_sdc:image-optional-with-example-and-additional-prop" at line 1.- After (#11)

Part three: out of scope
| Comment | File | Size | Author |
|---|---|---|---|
| #43 | Screenshot 2025-11-26 at 1.49.26 PM.png | 66.6 KB | mayur-sose |
| #28 | Screenshot 2025-11-13 at 5.39.55 PM.png | 34.42 KB | wim leers |
| #11 | content template optional image object prop.gif | 1.59 MB | wim leers |
| #9 | Screenshot 2025-10-16 at 1.01.15 PM.png | 41.08 KB | wim leers |
| #8 | after.png | 361.89 KB | wim leers |
Issue fork canvas-3541361
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
wim leersSibling issue: #3548292: Find required field instance matches for image (`json-schema-definitions://canvas.module/image`) and video prop shapes. Improving title here.
Comment #4
wim leersComment #5
wim leersPer #3548761-19: Can't link (pick a DynamicPropSource) to populate an optional explicit input if that DynamicPropSource evaluates to `NULL` (typically: field is empty), I think this just became more important.
Comment #6
nagwani commentedComment #8
wim leersComment #9
wim leersAs of #8 and commit https://git.drupalcode.org/project/canvas/-/merge_requests/242/diffs?com..., the match appears in the UI.
But upon selecting it, this happens:

The log entry:
Comment #10
wim leersThe log entry in #9 showed that
Evaluatorwas evaluatingFieldObjectPropsExpressions for an optional prop in a way that it'd just get{key1: null, key2: null …}. That's neither meaningful nor helpful.But the
Evaluatordid do its job correctly. The challenge is that SDC simply doesn't know how to handle that, which is also correct and reasonable. We could replace an optional prop that has an array of NULL values to just NULL, but that will triggerSo instead, we need to make
::hydrateComponent()smart enough to cross-reference the resolved value against the expected prop shape, and simply omit it: https://git.drupalcode.org/project/canvas/-/merge_requests/242/diffs?com...Comment #11
wim leersNo more render crash:
Comment #12
pameeela commentedJust tagging this for Drupal CMS because it'll help with Canvas-ing the remaining content types.
Comment #13
wim leersPicked this up again, ~2 weeks after I worked on this at DrupalCon. Still need to write tests.
But first: figure out why "Authored by → User picture" doesn't show up in the UI on
Nodes, when the test coverage proves that at least forUsers, "Use picture" does show up.Comment #14
wim leersComment #15
wim leers#13: root cause found, described in #2169813-25: Support deriving fields from entity definitions with multiple bundles (or zero bundles). Fixed in https://git.drupalcode.org/project/canvas/-/merge_requests/242/diffs?com....
This is definitely a generic problem, so retitling.
Comment #16
wim leersTest coverage complete. Next up: updating the matching/suggesting logic to omit the suggested
DynamicPropSourcesI flagged as "unwanted" in https://git.drupalcode.org/project/canvas/-/merge_requests/242/diffs?com...@lauriii: please confirm my hunches of what I expect you'll say:
'Authored by → User → Picture → Width'— https://git.drupalcode.org/project/canvas/-/merge_requests/242#note_615993'Silly image 🤡 → User ID → User → Picture— https://git.drupalcode.org/project/canvas/-/merge_requests/242/diffs#not...Authored bybut it actually points to a user picture, I think this needs to becomeAuthored by → Picture— https://git.drupalcode.org/project/canvas/-/merge_requests/242#note_615995Comment #17
wim leersLooks like I got the needed feedback 👍
Per #3555413-27: Allow linking to referenced entities: add `url` property to `EntityReferenceItem::propertyDefinitions()`, one more closely related edge case bug was identified.
Comment #18
lauriiiDoes this issue make it possible to link media images and videos with the image and video prop from Canvas?
Comment #19
wim leers#18: per https://git.drupalcode.org/project/canvas/-/merge_requests/242/diffs?com..., yes — that proves it for media images.
I'll expand the test coverage to verify that that is true for videos too 👍
Comment #20
wim leersPer @lauriii at https://git.drupalcode.org/project/canvas/-/merge_requests/242#note_618126, #3551339: Suggest only relevant DynamicPropSources should land first, because this is surfacing many new matches that are considered irrelevant.
Comment #21
wim leers#3551339: Suggest only relevant DynamicPropSources is in!
Comment #22
wim leersComponentSourceplugin, not in . To keep the commit history understandable, I will split this into two MRs:Comment #24
wim leersCreated https://git.drupalcode.org/project/canvas/-/merge_requests/319 for #22.2.2.
Note this closely relates to #3531905: Validation error on optional properties.: this "fixes" that problem in Canvas instead of in the SDC subsystem — but really, it's just Canvas now respecting the optionality of props.
Comment #25
wim leers— in #19, in response to @lauriii's question in #18. This already works for required video fields (see https://git.drupalcode.org/project/canvas/-/merge_requests/242#note_623498), test coverage should be added to verify it works for optional fields.
Will do that. If it doesn't turn out to be working, I will extract that into a follow-up issue, because then it is most likely not related to the problem described in the issue title.
Comment #26
wim leersTurns out #25 does not work 🫠 Debugging led to a new core bug 😬
Quoting core's
\Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::propertyDefinitions()The commit that added that comment indeed added that. But then #2577963: Let entity_ref Selection handlers be in charge of the field validation removed it, in favor of validation happening in selection handlers, specifically in
\Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection::validateReferenceableEntities()— see https://git.drupalcode.org/project/drupal/-/commit/fbe0991b98a34be226b55...This de facto means Typed Data incomplete; only when performing validation do we get to know which target bundles are actually allowed. That breaks scenarios like the one here. That means that all
EntityReferenceItemobjects'entityproperty are insufficiently constrained … probably due to #2169813: Support deriving fields from entity definitions with multiple bundles (or zero bundles)! 🫠😬 Because entity reference fields may target multiple bundles, but core'sEntityDataDefinitionis incapable of expressing that.So this puts us in a catch-22:
EntityDataDefinitionexposed by theentityproperty on entity reference fieldsTherefore the only choice remaining is to special-case
EntityReferenceItem.P.S.: in 2023, #3057545: ResourceTypeRepository wrongly assumes that all entity reference fields have the setting "target_type" introduced
\Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::getReferenceableBundles(), which is related to all this: it is a work-around for #2577963: Let entity_ref Selection handlers be in charge of the field validation, but A) requires extra manual function calls and hardcoded logic rather than the correct metadata being present in Typed Data, but also B) prepares core for Dynamic Entity Reference support (i.e. >1 target entity type).Comment #27
wim leersFailing test coverage for #18, which demonstrates in practice the problem outlined in #26: https://git.drupalcode.org/project/canvas/-/merge_requests/242/diffs?com...
Turns out I previously ran into this very challenge in July, specifically in #3524130: Define JSON Schema $refs for linking/embedding videos and linking documents's https://git.drupalcode.org/project/experience_builder/-/merge_requests/1..., where my write-up is describing the same problems as #22.
The analysis and solution there was simply incomplete: I fixed it then and there for multi-bundle entity reference fields, but failed to fix it for single-bundle entity reference fields 🫠
Fix: https://git.drupalcode.org/project/canvas/-/merge_requests/242/diffs?com...
👆 This entire set of work-arounds and the dozens of hours of debugging that led to them, could've been prevented if core had gotten this right a decade ago 😔 Because Drupal core's Typed Data descriptions are so inaccurate, Canvas has to introduce many work-arounds, and those work-arounds have to be careful not to tweak the actual Typed Data definitions in any way that could break core/contrib/custom code!
🤯 IOW: Canvas MUST keep Typed Data unchanged (to avoid BC breaks), and must convert/transform/upcast into the actually correct Typed Data (see:
BetterEntityDataDefinition,::getConstrainedTargetDefinition(), et cetera) to ensure correct matching. This will require a monumental effort to fix in core without breaking the ecosystem 🙈Comment #28
wim leersConfirmed that optional video fields are now possible to match:

Comment #29
wim leersSee the "ℹ️" comments on this very complex MR. 3 bugs are called out in headings. 2 of the 3 are core bugs 😅
The number of core bugs I’m running into is staggering … but on the other hand, it's understandable: Canvas is the first code to truly use the full potential of Typed Data. In a decade. So… makes sense.
The root cause of both core bugs is #2169813: Support deriving fields from entity definitions with multiple bundles (or zero bundles).
Given that nobody else knows enough about both core's Typed Data and Canvas' Shape matching … going ahead and merging this first MR. The test coverage proves it's working, the screenshots confirm it, and there's plenty of explanatory sprinkled to enable another person (one willing to learn enough about Typed Data) to understand what is happening, and what Canvas does to make things work as expected by end users.
Comment #31
wim leersBack to NW for https://git.drupalcode.org/project/canvas/-/merge_requests/319, to fix the component source bug this unveiled (see #9). Issue summary updated for clarity.
Comment #32
wim leersComment #33
wim leersDone. Thoroughly manually tested.
Even if I missed something, the 2 MRs that this issue landed represent a huge leap forward.
Comment #35
wim leers🚢
See you in #3557612: `::matchEntityPropsForObject()` is too naïve: nonsensical `type: object` shape matches and useless labels tomorrow.
Comment #37
lauriiiFor some reason I still don't see optional or required media images as an option to map to an image prop in Canvas. I've tried clearing caches. Am I missing some other steps to be able to use this?
Comment #38
wim leersIs it perhaps a multiple-cardinality field?
Comment #39
wim leersI bet I know what's going on. I bet you're testing with an "media" reference field referencing multiple media types, and each of those being of a different media source:
\Drupal\media\Plugin\media\Source\Imageand\Drupal\acquia_dam\Plugin\media\acquia_dam\Image.That today won't be found, because it requires the shape matching logic to:
field_media_(image|video)_file+field_media_(image|video)_file_1) of the same field type\Drupal\image\Plugin\Field\FieldType\ImageItemand\Drupal\acquia_dam\Plugin\Field\FieldType\AssetItem)Realized this while working on #3557612: `::matchEntityPropsForObject()` is too naïve: nonsensical `type: object` shape matches and useless labels, which is related. It also implicitly demonstrates the problem: the
media_video_fieldit tests with targets bothbaby_videosandvacation_videosand for'REQUIRED, type=object&$ref=json-schema-definitions://canvas.module/video2 different matches are found, which thanks to the prevention of less-efficient-but-equivalent matches has one usingbaby_videosand the other usingvacation_videos:If you can confirm that that is the situation you're testing with, I'm happy to create an issue for it. Suggested title: Support matching field instances (`DynamicPropSource`s) that contain multi-bundle references
Comment #40
lauriiiYep, this is the issue. Removing DAM image from the allowed bundles exposed the field in the list 👍 Good job for guessing the issue – next time is probably easier if I send a screenshot of the field configuration. 😅
Marking needs work for the follow-up which IMO doesn't have to be a stable blocker but still something we should work on as a high priority issue.
Comment #41
effulgentsia commentedWe're (ab)using "Patch (to be ported)" as the status for issues whose initial MR got merged but that still needs follow-ups created.
Comment #42
wim leersThanks for confirming. Will make that happen.
Comment #43
mayur-sose commented@wim-leers, I can see linked prop for image but not for videos, is this expected :
Comment #44
pameeela commentedRemoving our tag since we needed this just for simple image media.
Comment #45
wim leersAs of #3563309-3: [PP-1] Add support for matching against multi-bundle reference fields (e.g. a media field referencing 2 media types of different MediaSource plugins), #3563309's scope is precisely to make #39 & #40 possible. 👍