Describe the bug
add-data-source derives a tabular data source's identity (its alias in power.config.json, its schema file name, and its generated model/service names) from the leaf name only — the segment after the final dot. The [schema]. qualifier is stripped and never recovered.
Consequently, two different tables that share a base name in different schemas of the same database — [sales].[Contacts] and [ops].[Contacts] — both resolve to the alias contacts, and there is no --name / --alias parameter to disambiguate them.
The result is not an error. The second add:
- Overwrites
.power/schemas/sql/<dataset>/contacts.Schema.json with the second table's metadata, and regenerates ContactsModel / ContactsService from it;
- Leaves
power.config.json routing the contacts alias at the first table, because the dataset update is guarded by if (!dataSources.includes(alias)), which is already true.
So the app ships a service typed from [ops].[Contacts] but wired to [sales].[Contacts]. It compiles, it runs, and it returns the wrong rows. Nothing surfaces until someone notices the data is wrong.
This is distinct from the already-understood same-name-across-two-connections case. This is a single connection, a single dataset, two schemas.
Steps to Reproduce
CREATE SCHEMA sales;
CREATE SCHEMA ops;
CREATE TABLE sales.Contacts (Id int IDENTITY PRIMARY KEY, SalesOnlyColumn nvarchar(50));
CREATE TABLE ops.Contacts (Id int IDENTITY PRIMARY KEY, OpsOnlyColumn nvarchar(50));
pac code add-data-source -a "shared_sql" -c <connectionId> `
-d "myserver.database.windows.net,mydb" -t "[sales].[Contacts]"
pac code add-data-source -a "shared_sql" -c <connectionId> `
-d "myserver.database.windows.net,mydb" -t "[ops].[Contacts]"
Both commands report success. Then inspect:
.power/schemas/sql/myserver.database.windows.net,mydb/ — only one contacts.Schema.json, holding ops.Contacts.
src/generated/models/ContactsModel.ts — exposes OpsOnlyColumn; SalesOnlyColumn is gone.
power.config.json — one alias, still pointing at the first table:
"dataSets": {
"myserver.database.windows.net,mydb": {
"dataSources": {
"contacts": { "tableName": "[sales].[Contacts]" }
}
}
}
await ContactsService.getAll() now returns sales.Contacts rows against an ops.Contacts type.
Expected behavior
One of:
- Preferred — include the schema qualifier in the identity so the two tables are distinguishable (e.g. aliases
sales_contacts / ops_contacts, or feed the qualifier into the existing _1 disambiguation so the second becomes contacts_1); or
- Add an explicit
--name / --alias parameter so the caller can name the data source; or
- At minimum, fail loudly — refuse the second add with "a data source named
contacts already exists and points at [sales].[Contacts]" rather than producing a mismatched type/route pair.
Actual behavior
Both adds succeed. The generated code is typed from the second table, the routing points at the first, and the first table becomes unreachable from the app.
Root cause
Traced through the code-gen library. Paths below are relative to @microsoft/power-apps-actions/dist.
In 1.8.0 (current npm CLI, @microsoft/power-apps-cli@0.13.0):
CodeGen/shared/nameUtils.js:160 — sanitizeTabularResourceName() does result.match(/\.?([^.]*)$/), i.e. keeps only the segment after the final dot. [sales].[Contacts] → Contacts.
Actions/AddDataSource.js:386 — that value becomes tabularMetadata.friendlyName.
ReferenceStores/connectionReferences.js:36 — friendlyName becomes desiredName.
ReferenceStores/connectionReferences.js:191-193 — getExistingCdpNames() excludes every alias registered in the same dataset on the same reference, so getSanitizedUniqueName() never sees the existing contacts and never allocates contacts_1. That exclusion is intentional (it keeps re-adding the same table idempotent), but idempotency is keyed on the alias, and the alias no longer carries enough information to tell two tables apart.
ReferenceStores/connectionReferences.js:58 — the if (!dataSources.includes(dataSourceName)) guard then skips the dataSets update, so the routing keeps the first table.
Actions/AddDataSource.js saveMetadata() — writes ${dsName}.Schema.json unconditionally, overwriting the first schema file.
Secondary, pac-only: pac 2.9.3 bundles @microsoft/power-apps-actions 1.1.1, where the collision check is additionally case-sensitive — nameUtils.js:107 does nameSet.has(desiredName) with case preserved, against stored aliases that were lowercased on write. So on pac, _1 disambiguation never fires for any mixed-case table name, including the genuinely-different-connection and different-database cases that 1.8.0 handles correctly. 1.1.1 codegen (CodeGen/shared/modelUtils.js:39-44) also has no suffix support at all, so there is no way to recover by hand. Anyone on pac code is on this older path.
Environment information
- Framework / build tool: React + Vite
- Connector: SQL Server (
shared_sql), Azure SQL, cloud connection (no gateway)
pac CLI 2.9.3 → @microsoft/power-apps-actions 1.1.1
- Also verified against
@microsoft/power-apps-cli 0.13.0 → @microsoft/power-apps-actions 1.8.0
- OS: macOS
Additional context
Non-dbo schemas are normal in any database that isn't greenfield — multi-tenant or multi-domain schemas routinely repeat entity names (sales.Contacts, ops.Contacts). Silent data mis-binding is the worst available failure mode here; even without the full fix, (3) fail loudly would convert this from a wrong-data defect into a five-minute diagnosis.
Related but distinct:
Current workaround: create a wrapper view or synonym so the leaf names differ (CREATE VIEW dbo.vw_Ops_Contacts AS SELECT * FROM ops.Contacts). That requires DDL rights on the source database, which isn't always available.
Disclaimer: I investigated and drafted this issue with Claude Code (Anthropic) — it read the bundled @microsoft/power-apps-actions source in both shipped CLI paths and traced the mechanism behind the collision I hit in my own project. I've reviewed and verified the report before filing.
Describe the bug
add-data-sourcederives a tabular data source's identity (its alias inpower.config.json, its schema file name, and its generated model/service names) from the leaf name only — the segment after the final dot. The[schema].qualifier is stripped and never recovered.Consequently, two different tables that share a base name in different schemas of the same database —
[sales].[Contacts]and[ops].[Contacts]— both resolve to the aliascontacts, and there is no--name/--aliasparameter to disambiguate them.The result is not an error. The second add:
.power/schemas/sql/<dataset>/contacts.Schema.jsonwith the second table's metadata, and regeneratesContactsModel/ContactsServicefrom it;power.config.jsonrouting thecontactsalias at the first table, because the dataset update is guarded byif (!dataSources.includes(alias)), which is already true.So the app ships a service typed from
[ops].[Contacts]but wired to[sales].[Contacts]. It compiles, it runs, and it returns the wrong rows. Nothing surfaces until someone notices the data is wrong.This is distinct from the already-understood same-name-across-two-connections case. This is a single connection, a single dataset, two schemas.
Steps to Reproduce
Both commands report success. Then inspect:
.power/schemas/sql/myserver.database.windows.net,mydb/— only onecontacts.Schema.json, holdingops.Contacts.src/generated/models/ContactsModel.ts— exposesOpsOnlyColumn;SalesOnlyColumnis gone.power.config.json— one alias, still pointing at the first table:await ContactsService.getAll()now returnssales.Contactsrows against anops.Contactstype.Expected behavior
One of:
sales_contacts/ops_contacts, or feed the qualifier into the existing_1disambiguation so the second becomescontacts_1); or--name/--aliasparameter so the caller can name the data source; orcontactsalready exists and points at[sales].[Contacts]" rather than producing a mismatched type/route pair.Actual behavior
Both adds succeed. The generated code is typed from the second table, the routing points at the first, and the first table becomes unreachable from the app.
Root cause
Traced through the code-gen library. Paths below are relative to
@microsoft/power-apps-actions/dist.In 1.8.0 (current npm CLI,
@microsoft/power-apps-cli@0.13.0):CodeGen/shared/nameUtils.js:160—sanitizeTabularResourceName()doesresult.match(/\.?([^.]*)$/), i.e. keeps only the segment after the final dot.[sales].[Contacts]→Contacts.Actions/AddDataSource.js:386— that value becomestabularMetadata.friendlyName.ReferenceStores/connectionReferences.js:36—friendlyNamebecomesdesiredName.ReferenceStores/connectionReferences.js:191-193—getExistingCdpNames()excludes every alias registered in the same dataset on the same reference, sogetSanitizedUniqueName()never sees the existingcontactsand never allocatescontacts_1. That exclusion is intentional (it keeps re-adding the same table idempotent), but idempotency is keyed on the alias, and the alias no longer carries enough information to tell two tables apart.ReferenceStores/connectionReferences.js:58— theif (!dataSources.includes(dataSourceName))guard then skips thedataSetsupdate, so the routing keeps the first table.Actions/AddDataSource.jssaveMetadata()— writes${dsName}.Schema.jsonunconditionally, overwriting the first schema file.Secondary,
pac-only:pac2.9.3 bundles@microsoft/power-apps-actions1.1.1, where the collision check is additionally case-sensitive —nameUtils.js:107doesnameSet.has(desiredName)with case preserved, against stored aliases that were lowercased on write. So onpac,_1disambiguation never fires for any mixed-case table name, including the genuinely-different-connection and different-database cases that 1.8.0 handles correctly. 1.1.1 codegen (CodeGen/shared/modelUtils.js:39-44) also has no suffix support at all, so there is no way to recover by hand. Anyone onpac codeis on this older path.Environment information
shared_sql), Azure SQL, cloud connection (no gateway)pacCLI 2.9.3 →@microsoft/power-apps-actions1.1.1@microsoft/power-apps-cli0.13.0 →@microsoft/power-apps-actions1.8.0Additional context
Non-
dboschemas are normal in any database that isn't greenfield — multi-tenant or multi-domain schemas routinely repeat entity names (sales.Contacts,ops.Contacts). Silent data mis-binding is the worst available failure mode here; even without the full fix, (3) fail loudly would convert this from a wrong-data defect into a five-minute diagnosis.Related but distinct:
dboschema stored procedures generate a malformedoperationName. Same underlying theme: the schema qualifier is mishandled on the SQL path.Current workaround: create a wrapper view or synonym so the leaf names differ (
CREATE VIEW dbo.vw_Ops_Contacts AS SELECT * FROM ops.Contacts). That requires DDL rights on the source database, which isn't always available.Disclaimer: I investigated and drafted this issue with Claude Code (Anthropic) — it read the bundled
@microsoft/power-apps-actionssource in both shipped CLI paths and traced the mechanism behind the collision I hit in my own project. I've reviewed and verified the report before filing.