Title
Suffix *name virtual/mirror fields for lookups (e.g. trd_accountname, trd_truckname) are generated as if they were real Dataverse columns, but no such attribute exists, the display name only ever exists as a FormattedValue annotation on the lookup itself
Summary
For lookup columns, Dataverse exposes the related record's display name as a FormattedValue annotation attached to the lookup attribute itself (e.g. the raw record's trd_account attribute carries both the GUID and a FormattedValue of "BNP Paribas"). It does not create a separate, independently queryable attribute like trd_accountname for this purpose, that field simply doesn't exist anywhere in the table's real schema.
The Code Apps CLI's schema/type generator nonetheless synthesizes a sibling <lookup>name field (e.g. trd_accountname, trd_truckname, trd_contactname) for every lookup and adds it to the generated TypeScript type and JSON schema, making it look like a normal, populated string column. At runtime this field is always undefined/empty, because there is nothing on the wire (or in Dataverse metadata) to populate it from - the generator invented an attribute that Dataverse never creates.
Steps to Reproduce
- In a Dataverse table, add a lookup column (custom or standard) pointing to another table, e.g.
trd_account → account.
- In a Power Apps Code App project, add/refresh this table as a data source and inspect the generated schema, e.g.
.power/schemas/dataverse/<table>.Schema.json. Observe a synthesized field for the lookup's "name," e.g.:
"trd_accountname": {
"x-ms-dataverse-attribute": "trd_accountname",
"x-ms-schema-name": "trd_AccountName",
"x-ms-read-only": true
}
- Confirm this field does not correspond to any real column: query the table's actual attribute list (e.g. via the Dataverse metadata/describe tooling) -
trd_accountname is not present; only the lookup itself (trd_account) exists.
- Retrieve a record for this table that has the lookup populated with a valid, resolvable related record (e.g. confirm via SQL or the model-driven app that the Account lookup is genuinely set to "BNP Paribas").
- Pull the raw record data directly from Dataverse (e.g. via an SDK-style/organization-service record dump). Observe the related record's name is delivered as the
FormattedValue of the trd_account key itself - there is no separate trd_accountname key anywhere in the record's attributes or formatted values.
- In the Code App, call the generated service's
getAll()/get() for that table and read the trd_accountname field off the returned typed object.
- Observe the field is
undefined/empty in the app, despite the lookup (trd_account) being valid and resolving to a real, named record.
Expected Behavior
Either:
- (a) the generator should not synthesize a non-existent
<lookup>name field at all, or
- (b) if it does synthesize one for developer convenience, it should populate it at runtime from the lookup attribute's own
FormattedValue/@OData.Community.Display.V1.FormattedValue annotation (the only place this data actually exists), so the convenience field behaves as documented.
Actual Behavior
The generated type/schema includes a <lookup>name field that looks like a normal, dependable string property, but it is never populated - it silently returns undefined regardless of whether the lookup is set, because Dataverse never emits a matching attribute or annotation under that name.
Supporting Evidence
- Dataverse-side verification (SQL join) confirms the lookup and its target record both exist and are correctly linked - e.g. Order "Crêpes de Paris - 03/09/2026 17:49:01" has
trd_account resolving to real Account "BNP Paribas" and trd_truck resolving to real Food Truck "Crêpes de Paris". Also confirmed directly in the model-driven app form (Account/Truck lookups clearly populated).
- A raw record dump for that same order shows the related names delivered on the lookup's own key, not on any
*name sibling:
Attributes: "trd_account": {Name: "BNP Paribas"}, "trd_truck": {Name: "Crêpes de Paris"}
FormattedValues: "trd_account": "BNP Paribas", "trd_truck": "Crêpes de Paris"
No trd_accountname or trd_truckname key is present anywhere in the record.
- The table's real column list (from Dataverse metadata) contains only
trd_account, trd_contact, trd_truck as lookups - no trd_accountname/trd_contactname/trd_truckname columns exist.
- The generated schema (
orders.Schema.json) nonetheless lists these fabricated fields with a dual-casing pattern also seen elsewhere in this generator (x-ms-dataverse-attribute: trd_accountname vs. guessed x-ms-schema-name: trd_AccountName), suggesting the same underlying convention-guessing logic that produces the separately-reported @odata.bind casing bug is also responsible for inventing these non-existent mirror fields.
Environment
@microsoft/power-apps-cli: 1.0.1
@microsoft/power-apps: 1.2.5
@microsoft/power-apps-vite: 1.0.2
- Dataverse online environment
- Reproduced with both custom lookups (
trd_account, trd_truck, created via Dataverse Web API metadata calls rather than the maker-portal column designer) - root cause likely generic to how the generator handles all lookup columns, not specific to custom vs. standard.
Suggested Fix / Workaround
- The generator should stop assuming a
<lookup>name sibling attribute exists for every lookup. If retained for developer convenience, it should be populated from the lookup's own FormattedValue annotation at runtime, not treated as an independent schema field.
- Workaround: don't rely on the generated
*name fields. Read the raw lookup GUID (trd_account, trd_truck, etc.) and resolve display names client-side against a small id→name map loaded once from the related data source (Accounts, Food Trucks, etc.).
Title
Suffix
*namevirtual/mirror fields for lookups (e.g.trd_accountname,trd_truckname) are generated as if they were real Dataverse columns, but no such attribute exists, the display name only ever exists as aFormattedValueannotation on the lookup itselfSummary
For lookup columns, Dataverse exposes the related record's display name as a
FormattedValueannotation attached to the lookup attribute itself (e.g. the raw record'strd_accountattribute carries both the GUID and aFormattedValueof"BNP Paribas"). It does not create a separate, independently queryable attribute liketrd_accountnamefor this purpose, that field simply doesn't exist anywhere in the table's real schema.The Code Apps CLI's schema/type generator nonetheless synthesizes a sibling
<lookup>namefield (e.g.trd_accountname,trd_truckname,trd_contactname) for every lookup and adds it to the generated TypeScript type and JSON schema, making it look like a normal, populated string column. At runtime this field is alwaysundefined/empty, because there is nothing on the wire (or in Dataverse metadata) to populate it from - the generator invented an attribute that Dataverse never creates.Steps to Reproduce
trd_account→account..power/schemas/dataverse/<table>.Schema.json. Observe a synthesized field for the lookup's "name," e.g.:trd_accountnameis not present; only the lookup itself (trd_account) exists.FormattedValueof thetrd_accountkey itself - there is no separatetrd_accountnamekey anywhere in the record's attributes or formatted values.getAll()/get()for that table and read thetrd_accountnamefield off the returned typed object.undefined/empty in the app, despite the lookup (trd_account) being valid and resolving to a real, named record.Expected Behavior
Either:
<lookup>namefield at all, orFormattedValue/@OData.Community.Display.V1.FormattedValueannotation (the only place this data actually exists), so the convenience field behaves as documented.Actual Behavior
The generated type/schema includes a
<lookup>namefield that looks like a normal, dependable string property, but it is never populated - it silently returnsundefinedregardless of whether the lookup is set, because Dataverse never emits a matching attribute or annotation under that name.Supporting Evidence
trd_accountresolving to real Account "BNP Paribas" andtrd_truckresolving to real Food Truck "Crêpes de Paris". Also confirmed directly in the model-driven app form (Account/Truck lookups clearly populated).*namesibling:trd_accountnameortrd_trucknamekey is present anywhere in the record.trd_account,trd_contact,trd_truckas lookups - notrd_accountname/trd_contactname/trd_trucknamecolumns exist.orders.Schema.json) nonetheless lists these fabricated fields with a dual-casing pattern also seen elsewhere in this generator (x-ms-dataverse-attribute: trd_accountnamevs. guessedx-ms-schema-name: trd_AccountName), suggesting the same underlying convention-guessing logic that produces the separately-reported@odata.bindcasing bug is also responsible for inventing these non-existent mirror fields.Environment
@microsoft/power-apps-cli: 1.0.1@microsoft/power-apps: 1.2.5@microsoft/power-apps-vite: 1.0.2trd_account,trd_truck, created via Dataverse Web API metadata calls rather than the maker-portal column designer) - root cause likely generic to how the generator handles all lookup columns, not specific to custom vs. standard.Suggested Fix / Workaround
<lookup>namesibling attribute exists for every lookup. If retained for developer convenience, it should be populated from the lookup's ownFormattedValueannotation at runtime, not treated as an independent schema field.*namefields. Read the raw lookup GUID (trd_account,trd_truck, etc.) and resolve display names client-side against a small id→name map loaded once from the related data source (Accounts, Food Trucks, etc.).