Summary
Every mxcli command that re-serialises an existing persistent entity resets each attribute's second (storage/runtime) GUID to the element $ID. The Mendix runtime's database synchroniser identifies columns by that storage GUID (mendixsystem$attribute.id), so on the next deploy it treats every attribute as "deleted + new attribute added": it drops the old columns and creates empty ones. All attribute values are lost; rows and associations survive.
No warning is emitted by mxcli, mx check, or the deployment pipeline. The problem only becomes visible when the package is deployed to a database that already contains data.
Impact
Production deploy on 2026-09-11 wiped every attribute (Name, URLName, Status, … 28 attributes) of all 607 rows of a Masterdata.Organization entity after a single MDL edit of that entity. Recovery required a DB restore plus a byte-level patch of the .mxunit to restore the original GUIDs.
Environment
- mxcli nightly build of 2026-09-08 (Windows, amd64)
- Mendix 10.x project (MPR v2,
mprcontents/**/*.mxunit)
- PostgreSQL database with existing data
Steps to reproduce
- Take a project with a persistent entity that already has data in a deployed database.
- Export the attribute storage IDs from the deployed DB and note the
id values for the entity's attributes:
pg_restore -t 'mendixsystem$attribute' -f meta.sql <backup>
- Run any of the following against the entity (each one was tested separately on a fresh copy of the project):
CREATE OR MODIFY ENTITY Module.Entity (...)
ALTER ENTITY Module.Entity ADD ATTRIBUTE ...
ALTER ENTITY Module.Entity MODIFY ATTRIBUTE ...
ALTER ENTITY Module.Entity RENAME ATTRIBUTE ...
ALTER ENTITY Module.Entity DROP ATTRIBUTE ...
ALTER ENTITY Module.Entity SET DOCUMENTATION '...'
- Inspect the entity's
.mxunit (BSON) again: every attribute now has its storage GUID equal to its $ID. In our case 0 of 28 original storage GUIDs survived, for every command above — including SET DOCUMENTATION, which touches no attribute at all.
- Build and deploy to the database from step 1. The runtime logs the attributes as removed/added and drops + recreates the columns.
Commands that did not touch the existing GUIDs (27/28 kept — only the newly added items differ):
CREATE PERSISTENT ENTITY ... (new entity)
CREATE ASSOCIATION ... pointing at the existing entity
Expected behaviour
Re-serialising an entity must preserve the storage GUID of every attribute that was not explicitly dropped. Only newly created attributes should get a fresh GUID. Ideally the CLI should also refuse (or at least warn loudly) when a write would change the storage GUID of an existing attribute.
Actual behaviour
Storage GUID is overwritten with $ID on every write, silently.
Technical notes
Each attribute in the unit carries two GUIDs: the element $ID and a second GUID that the runtime uses as mendixsystem$attribute.id. In a Studio Pro–authored unit these differ; after an mxcli write they are identical. We recovered by byte-patching the 28 original GUIDs back into the unit (.NET GUID byte order) and recomputing the unit's ContentsHash = base64(sha256(unit bytes)) in the .mpr Unit table, then deploying — data was intact afterwards, confirming the GUID is the only thing the synchroniser keys on.
Happy to provide a minimal .mpr + before/after unit dumps if useful.
Summary
Every mxcli command that re-serialises an existing persistent entity resets each attribute's second (storage/runtime) GUID to the element
$ID. The Mendix runtime's database synchroniser identifies columns by that storage GUID (mendixsystem$attribute.id), so on the next deploy it treats every attribute as "deleted + new attribute added": it drops the old columns and creates empty ones. All attribute values are lost; rows and associations survive.No warning is emitted by mxcli,
mx check, or the deployment pipeline. The problem only becomes visible when the package is deployed to a database that already contains data.Impact
Production deploy on 2026-09-11 wiped every attribute (Name, URLName, Status, … 28 attributes) of all 607 rows of a
Masterdata.Organizationentity after a single MDL edit of that entity. Recovery required a DB restore plus a byte-level patch of the.mxunitto restore the original GUIDs.Environment
mprcontents/**/*.mxunit)Steps to reproduce
idvalues for the entity's attributes:CREATE OR MODIFY ENTITY Module.Entity (...)ALTER ENTITY Module.Entity ADD ATTRIBUTE ...ALTER ENTITY Module.Entity MODIFY ATTRIBUTE ...ALTER ENTITY Module.Entity RENAME ATTRIBUTE ...ALTER ENTITY Module.Entity DROP ATTRIBUTE ...ALTER ENTITY Module.Entity SET DOCUMENTATION '...'.mxunit(BSON) again: every attribute now has its storage GUID equal to its$ID. In our case 0 of 28 original storage GUIDs survived, for every command above — includingSET DOCUMENTATION, which touches no attribute at all.Commands that did not touch the existing GUIDs (27/28 kept — only the newly added items differ):
CREATE PERSISTENT ENTITY ...(new entity)CREATE ASSOCIATION ...pointing at the existing entityExpected behaviour
Re-serialising an entity must preserve the storage GUID of every attribute that was not explicitly dropped. Only newly created attributes should get a fresh GUID. Ideally the CLI should also refuse (or at least warn loudly) when a write would change the storage GUID of an existing attribute.
Actual behaviour
Storage GUID is overwritten with
$IDon every write, silently.Technical notes
Each attribute in the unit carries two GUIDs: the element
$IDand a second GUID that the runtime uses asmendixsystem$attribute.id. In a Studio Pro–authored unit these differ; after an mxcli write they are identical. We recovered by byte-patching the 28 original GUIDs back into the unit (.NET GUID byte order) and recomputing the unit'sContentsHash= base64(sha256(unit bytes)) in the.mprUnittable, then deploying — data was intact afterwards, confirming the GUID is the only thing the synchroniser keys on.Happy to provide a minimal
.mpr+ before/after unit dumps if useful.