Skip to content

[Bug] npx power-apps push uploads files to wrong blob path on Windows — player returns 403 InvalidAppBlobRequest #332

@This-Is-NPC

Description

@This-Is-NPC

On Windows, npx power-apps push reports success and creates/updates the app in the target environment, but the player URL returns HTTP 403 with:

{
  "error": {
    "code": "InvalidAppBlobRequest",
    "message": "The requested file is not associated with any known app."
  }
}

Root cause is a path-separator handling bug in @microsoft/power-apps-actions (transitive dep of @microsoft/power-apps-cli): files are uploaded under <blob>/<buildPath>/<file> while the app metadata's documentUri points at <blob>/<buildEntryPoint> (no prefix). The two never match, so the player can't resolve any blob owned by the app.

The bug is silent at push time — the upload succeeds and the CLI even prints a play URL — so it's easy to misdiagnose as auth, sharing, or content/manifest issues.

Environment

OS Windows 11 Pro 10.0.26200
Shell PowerShell 7
Node LTS (v22.x)
@microsoft/power-apps-cli 0.11.0
@microsoft/power-apps-actions 1.5.0
@microsoft/power-apps 1.1.3
Region Europe
Env Type Production

Reproduction

  1. On Windows, scaffold a code app and configure APP/power.config.json with a nested buildPath, e.g.:
    {
      "buildPath": "./dist/wc-dc-BaseProject/browser",
      "buildEntryPoint": "index.html",
      ...
    }
  2. Build the app so dist/wc-dc-BaseProject/browser/index.html exists.
  3. Run npx power-apps push.
  4. Open the play URL printed by the CLI.

Expected

App loads in the player.

Actual

CLI prints App pushed successfully. You can play your app at https://apps.powerapps.com/play/... but the player URL returns:

GET .../index.html → 403
{"error":{"code":"InvalidAppBlobRequest","message":"The requested file is not associated with any known app."}}

Root cause

In @microsoft/power-apps-actions/dist/Actions/PushApp.js, function uploadFilesToBlob:

const { buildEntryPoint, buildPath } = powerConfig;
// buildPath e.g. "./dist/wc-dc-BaseProject/browser"

const allFiles = await collectAllFiles(buildPath, vfs);
// On Windows, `vfs.join` is `path.join`, which uses `\` —
// fullFilePath ends up like "dist\\wc-dc-BaseProject\\browser\\index.html".

const normalizedBuildPath = buildPath.replace(/^\.\//, '');
// "dist/wc-dc-BaseProject/browser" — forward slashes preserved from config.

for (const fullFilePath of allFiles) {
    // ...
    // BUG: the buildPath prefix is stripped BEFORE backslashes are normalized.
    const relativePath = fullFilePath
        .replace(normalizedBuildPath, '')   // ← forward-slash prefix never matches Windows backslash path
        .replace(/\\/g, '/')
        .replace(/^\//, '');

    const url = `${baseUrl}/${relativePath}${restUrl}`;
    await httpClient.put(url, { ... });
}

// Meanwhile, documentUri uses buildEntryPoint, which has NO buildPath prefix
// to begin with, so the (broken) replace is a no-op for it:
return `${baseUrl}/${
    buildEntryPoint.replace(normalizedBuildPath, '').replace(/^\//, '')
}${restUrl}`;
// → documentUri = `${baseUrl}/index.html${restUrl}`

So on Windows:

What Resulting URL
File uploaded for index.html <baseUrl>/dist/wc-dc-BaseProject/browser/index.html<sas>
documentUri saved into app metadata <baseUrl>/index.html<sas>

The player resolves the metadata's documentUri, hits a path that has no blob, and the storage layer responds with InvalidAppBlobRequest.

CliFs.join source confirming the separator: node_modules/@microsoft/power-apps-cli/dist/FS/CliFs.js:

join(...paths) {
    return path.join(...paths);
}

Workaround

Until a fix ships, apply the diff above directly to node_modules/@microsoft/power-apps-actions/dist/Actions/PushApp.js. To survive npm install, use patch-package:

npm install --save-dev patch-package
# manually edit node_modules/@microsoft/power-apps-actions/dist/Actions/PushApp.js with the diff above
npx patch-package @microsoft/power-apps-actions
# add "postinstall": "patch-package" to package.json scripts, then commit the generated patches/ folder

After this, every npm install re-applies the fix automatically.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions