You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ALTER PAGE … REPLACE / INSERT BEFORE|AFTER resolve the new widget's attribute bindings against the wrong data context when the target sits under a selection-driven data view or a flow-sourced pluggable list — mxcli check and exec clean, model breaks at mx check (CE1613 / CE0402) #1076
ALTER PAGE … REPLACE <widget> WITH { … } (and the sibling INSERT BEFORE / INSERT AFTER) resolve
the new widgets' attribute references through Mutator.EnclosingEntity(target). That walk
(findEnclosingEntityContext → findEntityContextInWidgets → widgetOwnEntity) takes an entity
from a container only when its DataSource document carries an EntityRef. Two common page
shapes contribute no EntityRef, so the walk skips the target's real scope and keeps
whatever it last saw:
Target sits inside
Stored as
Walk yields
Symptom at mx check
a selection-driven data view (DataSource: SELECTION <list>), nested in a page-level data view over another entity
Forms$ListenTargetSource — no EntityRef
the outer page-level entity
CE1613 "The selected attribute 'Module.Outer.Attr' no longer exists." — the binding is re-scoped to an entity that does not have the attribute
a gallery / DataGrid 2 template whose list is sourced by a microflow, nanoflow, or an association without a direct EntityRef, on a page with no outer data view
pluggable datasource property, no EntityRef.Entity
empty
CE0402 "No value specified." — DESCRIBE PAGE shows ContentParams: [{1} = <unbound>]
In both cases mxcli check --references passes, mxcli exec reports success, and in the
re-scope case DESCRIBE PAGE even prints the intended attribute back correctly, so nothing on the
mxcli side shows the damage. It surfaces at mxbuild / Studio Pro load, after the write has
landed — on the first project that meant the running app was down until the snapshot was
restored.
Widgets authored in the same position by CREATE PAGE are bound correctly, and a REPLACE whose
target is a direct child of the page body is bound correctly too. The defect is specific to the
enclosing-scope walk used by the ALTER PAGE mutations.
Environment
mxcli v0.20.0 (reproduction 1, both occurrences) and 39c7d94 / 2026-08-25 (reproduction 2); the walker is unchanged at HEAD 191a0c9 (2026-09-07) and the release notes through 0.19.0 carry nothing for it
Reproduction 1 — selection-driven data view: binding re-scoped to the OUTER entity (CE1613)
Page shape: a page-level data view over Dashboard, containing a list over its versions and a
selection-driven data view over DashboardVersion, which holds a dynamictext bound to an
attribute of DashboardVersion.
The stored reference is Dashboard.PeriodLabel — the page's outer entity — not DashboardVersion.PeriodLabel. Seen twice on the source project on two different pages
(txtPreviewPeriod inside a selection data view; txtVersionMeta inside a gallery template with ContentParams: [{1} = SizeLabel, {2} = UploadedAt], both re-resolved to the outer Dashboard).
Reproduction 2 — gallery template, no outer data view: binding dropped (CE0402)
./mxcli check bug118-replace.mdl -p Test.mpr --references # Check passed!
./mxcli exec bug118-replace.mdl -p Test.mpr # Altered page — no error
./mxcli -p Test.mpr -c "DESCRIBE PAGE BUG118.Page_Gallery"# -> dynamictext txtName2 (Content: '{1}', ContentParams: [{1} = <unbound>])~/.mxcli/mxbuild/*/modeler/mx check Test.mpr
# [error] [CE0402] "No value specified." at Text 'txtName2'
Same result with the explicitly qualified form ContentParams: [{1} = $currentObject/Name], which
rules out attribute rooting as the cause. The original template widget (txtName, written by CREATE PAGE) was bound correctly; only the REPLACE-introduced widget loses it. Reproduced on the
2026-08-25 build; note that on v0.20.0 a database-sourced gallery may already resolve (see
"Where it is in the code" — entityFromEntityRef was extended for association sources), so run
this reproduction with the gallery sourced by a microflow returning List of BUG118.Item if the
database form has since been fixed. The selection-driven data view of reproduction 1 fails on
v0.20.0 regardless.
Where it is in the code (mdl/backend/pagemutator/mutator.go, HEAD 191a0c9)
Mutator.EnclosingEntity → findEnclosingEntityContext → findEntityContextInWidgets: the
recursive walk carries currentEntity down and replaces it only when widgetOwnEntity(wDoc)
returns non-empty.
widgetOwnEntity = extractEntityFromDataSource (reads DataSource.EntityRef only) or extractPluggableDataSourceEntity (reads the pluggable datasource property's DataSource.EntityRef only).
A selection-driven data view stores DataSource as Forms$ListenTargetSource with a ListenTarget widget reference and no EntityRef, so widgetOwnEntity returns "" and the
walk keeps the outer entity → reproduction 1.
A flow-sourced list stores MicroflowSettings / NanoflowSettings, also without EntityRef. EnclosingDataSourceFlow (FINDINGS Security - Review against OpenSSF Open Source Project Security Baseline #55) covers the flow case only for the nearest data source,
and only where the caller consults it; the gallery-template path in reproduction 2 still came
back empty → <unbound>.
CREATE PAGE does not go through this walk, which is why the same widget in the same position
is bound correctly at page creation.
Expected behavior
The replacement / inserted widget's attribute references resolve against the target's nearest
enclosing data context, exactly as CREATE PAGE resolves them in the same position and as DESCRIBE PAGE prints them back. Concretely:
widgetOwnEntity (or the walk around it) resolves a Forms$ListenTargetSource by following ListenTarget to the referenced list widget and taking that widget's entity.
Flow-sourced containers resolve through the flow's return type at every level of the walk, not
only for the nearest source.
When the walk cannot determine the context, mxcli check --references / exec should fail
loudly instead of writing an <unbound> or outer-scoped reference — a page mutation that
cannot bind its attribute has no correct output to write.
Actual behavior
Reproduction 1: binding silently re-scoped to the page's outer entity; every mxcli-side read
(including DESCRIBE PAGE) shows the intended attribute, and the model fails to build.
Reproduction 2: binding silently dropped (<unbound>), visible in DESCRIBE PAGE, model fails mx check.
Severity
High. Silent through check --references and exec; in the re-scope case invisible to DESCRIBE PAGE as well. On the first project the failed build overwrote the previous
deployment, so the running app was down until the .mpr snapshot was restored.
Workaround
Do not REPLACE / INSERT a widget carrying an attribute binding when the target sits under a
selection-driven data view or a pluggable list template. If the same attribute stays bound and
only the text changes, SET Content = '…' ON <widget> leaves the existing binding untouched and
is unaffected (the first project's actual fix).
A genuine rebind has no narrower ALTER PAGE form today — SET ContentParams = […] and the
per-index spellings are hard parse errors — so it needs CREATE OR REPLACE PAGE or Studio Pro.
Gate every ALTER PAGE on the real mx check, not on mxcli check --references alone.
Checked against HEAD ca9b90e (2026-09-08, merge of #1075): the only commit to mdl/backend/pagemutator/mutator.go since 191a0c9 is c9cabc4 (pluggable property lookup by name, unrelated). widgetOwnEntity, extractEntityFromDataSource and extractPluggableDataSourceEntity still read EntityRef only; the sole ListenTarget occurrence in the package is the write in serializeDataSourceBson (Forms$ListenTargetSource with a ListenTarget key and no EntityRef), and nothing in the walk resolves it. Not fixed.
Summary
ALTER PAGE … REPLACE <widget> WITH { … }(and the siblingINSERT BEFORE/INSERT AFTER) resolvethe new widgets' attribute references through
Mutator.EnclosingEntity(target). That walk(
findEnclosingEntityContext→findEntityContextInWidgets→widgetOwnEntity) takes an entityfrom a container only when its
DataSourcedocument carries anEntityRef. Two common pageshapes contribute no
EntityRef, so the walk skips the target's real scope and keepswhatever it last saw:
mx checkDataSource: SELECTION <list>), nested in a page-level data view over another entityForms$ListenTargetSource— noEntityRefCE1613 "The selected attribute 'Module.Outer.Attr' no longer exists."— the binding is re-scoped to an entity that does not have the attributeEntityRef, on a page with no outer data viewdatasourceproperty, noEntityRef.EntityCE0402 "No value specified."—DESCRIBE PAGEshowsContentParams: [{1} = <unbound>]In both cases
mxcli check --referencespasses,mxcli execreports success, and in there-scope case
DESCRIBE PAGEeven prints the intended attribute back correctly, so nothing on themxcli side shows the damage. It surfaces at
mxbuild/ Studio Pro load, after the write haslanded — on the first project that meant the running app was down until the snapshot was
restored.
Widgets authored in the same position by
CREATE PAGEare bound correctly, and aREPLACEwhosetarget is a direct child of the page body is bound correctly too. The defect is specific to the
enclosing-scope walk used by the ALTER PAGE mutations.
Environment
39c7d94/ 2026-08-25 (reproduction 2); the walker is unchanged at HEAD191a0c9(2026-09-07) and the release notes through 0.19.0 carry nothing for itReproduction 1 — selection-driven data view: binding re-scoped to the OUTER entity (CE1613)
Page shape: a page-level data view over
Dashboard, containing a list over its versions and aselection-driven data view over
DashboardVersion, which holds adynamictextbound to anattribute of
DashboardVersion.Now replace the inner widget with the same binding:
The stored reference is
Dashboard.PeriodLabel— the page's outer entity — notDashboardVersion.PeriodLabel. Seen twice on the source project on two different pages(
txtPreviewPeriodinside a selection data view;txtVersionMetainside a gallery template withContentParams: [{1} = SizeLabel, {2} = UploadedAt], both re-resolved to the outerDashboard).Reproduction 2 — gallery template, no outer data view: binding dropped (CE0402)
Same result with the explicitly qualified form
ContentParams: [{1} = $currentObject/Name], whichrules out attribute rooting as the cause. The original template widget (
txtName, written byCREATE PAGE) was bound correctly; only the REPLACE-introduced widget loses it. Reproduced on the2026-08-25 build; note that on v0.20.0 a database-sourced gallery may already resolve (see
"Where it is in the code" —
entityFromEntityRefwas extended for association sources), so runthis reproduction with the gallery sourced by a microflow returning
List of BUG118.Itemif thedatabase form has since been fixed. The selection-driven data view of reproduction 1 fails on
v0.20.0 regardless.
Where it is in the code (
mdl/backend/pagemutator/mutator.go, HEAD191a0c9)Mutator.EnclosingEntity→findEnclosingEntityContext→findEntityContextInWidgets: therecursive walk carries
currentEntitydown and replaces it only whenwidgetOwnEntity(wDoc)returns non-empty.
widgetOwnEntity=extractEntityFromDataSource(readsDataSource.EntityRefonly) orextractPluggableDataSourceEntity(reads the pluggabledatasourceproperty'sDataSource.EntityRefonly).DataSourceasForms$ListenTargetSourcewith aListenTargetwidget reference and noEntityRef, sowidgetOwnEntityreturns""and thewalk keeps the outer entity → reproduction 1.
MicroflowSettings/NanoflowSettings, also withoutEntityRef.EnclosingDataSourceFlow(FINDINGS Security - Review against OpenSSF Open Source Project Security Baseline #55) covers the flow case only for the nearest data source,and only where the caller consults it; the gallery-template path in reproduction 2 still came
back empty →
<unbound>.CREATE PAGEdoes not go through this walk, which is why the same widget in the same positionis bound correctly at page creation.
Expected behavior
The replacement / inserted widget's attribute references resolve against the target's nearest
enclosing data context, exactly as
CREATE PAGEresolves them in the same position and asDESCRIBE PAGEprints them back. Concretely:widgetOwnEntity(or the walk around it) resolves aForms$ListenTargetSourceby followingListenTargetto the referenced list widget and taking that widget's entity.only for the nearest source.
mxcli check --references/execshould failloudly instead of writing an
<unbound>or outer-scoped reference — a page mutation thatcannot bind its attribute has no correct output to write.
Actual behavior
Reproduction 1: binding silently re-scoped to the page's outer entity; every mxcli-side read
(including
DESCRIBE PAGE) shows the intended attribute, and the model fails to build.Reproduction 2: binding silently dropped (
<unbound>), visible inDESCRIBE PAGE, model failsmx check.Severity
High. Silent through
check --referencesandexec; in the re-scope case invisible toDESCRIBE PAGEas well. On the first project the failed build overwrote the previousdeployment, so the running app was down until the
.mprsnapshot was restored.Workaround
REPLACE/INSERTa widget carrying an attribute binding when the target sits under aselection-driven data view or a pluggable list template. If the same attribute stays bound and
only the text changes,
SET Content = '…' ON <widget>leaves the existing binding untouched andis unaffected (the first project's actual fix).
SET ContentParams = […]and theper-index spellings are hard parse errors — so it needs
CREATE OR REPLACE PAGEor Studio Pro.mx check, not onmxcli check --referencesalone.Checked against HEAD
ca9b90e(2026-09-08, merge of #1075): the only commit tomdl/backend/pagemutator/mutator.gosince191a0c9isc9cabc4(pluggable property lookup by name, unrelated).widgetOwnEntity,extractEntityFromDataSourceandextractPluggableDataSourceEntitystill readEntityRefonly; the soleListenTargetoccurrence in the package is the write inserializeDataSourceBson(Forms$ListenTargetSourcewith aListenTargetkey and noEntityRef), and nothing in the walk resolves it. Not fixed.