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
{{ message }}
Repository navigation
Debug ID changes the bytes of polyfills and main but not their file names, so cached files fail the SRI check #34674
The builder names polyfills-<hash>.js and main-<hash>.js first. Then it adds a //# debugId=<id> line to the file. It computes <id> from the source map after it rewrites the map to add x_google_ignoreList. The file name does not cover that line.
So when a builder release writes the source map differently, the JS bytes change and the file name stays the same. 22.1.8 did that. Commit 5a0d226 moved x_google_ignoreList to the start of the map.
With subresourceIntegrity, the new index.html pins the new bytes. A CDN or browser cache that kept the old file under the same URL serves the old bytes, and the browser blocks the script. This happened to us in production with polyfills-<hash>.js.
#34583 fixed this for the chunks of the chunk optimizer. It does not cover files that esbuild names, such as main and polyfills. 22.2.6 still computes their debug ID from the rewritten map.
Minimal Reproduction
npx @angular/cli@22.1.8 new repro --defaults --ssr=false
cd repro
npm install zone.js
npm install -D @angular/build@22.1.6
npx ng config projects.repro.architect.build.options.polyfills '["zone.js"]'
npx ng config projects.repro.architect.build.configurations.production.sourceMap true
npx ng config projects.repro.architect.build.configurations.production.subresourceIntegrity true
npx ng build && mv dist/repro/browser ../build-22.1.6
npm install -D @angular/build@22.1.8
npx ng build && mv dist/repro/browser ../build-22.1.8
Both builds write polyfills-QWJLBG44.js and main-HHND32NM.js. In each file, only the //# debugId= line differs:
The two index.html files pin different hashes for the same URLs. For both files, the two source maps have the same content in a different key order.
Exception or Error
Failed to find a valid digest in the 'integrity' attribute for resource 'https://example.com/polyfills-JTOAPWZJ.js' with computed SHA-384 integrity '...'. The resource has been blocked.
Expected: when the bytes of a hashed file change, its name changes too.
A possible fix is to compute the debug ID from the JS before the builder adds the debug ID line, not from the rewritten map. The file name already covers the JS.
changed the title [-]Debug ID changes the bytes of main and polyfills but not their file names, so cached files fail the SRI check[/-][+]Debug ID changes the bytes of polyfills and main but not their file names, so cached files fail the SRI check[/+]on Nov 8, 2026
The same symptom has a second cause: the entry file of the chunk optimizer. The debug ID fix above does not fix it.
The chunk optimizer runs when an app has 3 or more lazy chunks. It renames the lazy chunks, and main imports them by name. But the optimizer passes chunkFileNames and no entryFileNames, so main keeps the name that esbuild gave it.
So when a builder release changes how the optimizer names chunks, the bytes of main change and its name stays the same. 22.2.6 did that with #34583.
npx @angular/cli@22.2.5 new repro --defaults --ssr=false
cd repro
npm install -D @angular/build@22.2.5
npx ng generate component a
npx ng generate component b
npx ng generate component c
cat > src/app/app.routes.ts <<'EOF'import { Routes } from '@angular/router';export const routes: Routes = [ { path: 'a', loadComponent: () => import('./a/a').then((m) => m.A) }, { path: 'b', loadComponent: () => import('./b/b').then((m) => m.B) }, { path: 'c', loadComponent: () => import('./c/c').then((m) => m.C) },];EOF
npx ng config projects.repro.architect.build.configurations.production.sourceMap true
npx ng config projects.repro.architect.build.configurations.production.subresourceIntegrity true
npx ng build && mv dist/repro/browser ../build-22.2.5
npm install -D @angular/build@22.2.6
npx ng build && mv dist/repro/browser ../build-22.2.6
Both builds write main-5H54S2WB.js, and the two index.html files pin different hashes for it. The only difference in the file is the names of the chunks it imports:
The debug ID and the source map of main are the same in both builds.
A possible fix is to give the entry a content hash with entryFileNames, the same way as chunkFileNames, and to update index.html with the new name. A fix that computes every file name from its final bytes would cover both causes.
I looked at how to fix both causes. The fix for the second cause is small. The fix for the first cause has a trade-off that a maintainer should decide.
The rule I want to keep is that when the bytes of a hashed file change, its name changes too. That rule must also hold on the upgrade to the release that contains the fix.
Second cause: the entry file of the chunk optimizer
The fix passes entryFileNames to the three generate() calls in chunk-optimizer.ts, so main gets a content hash when bundles are hashed. initialFiles then uses the new name. With outputHashing: none, the file stays main.js.
When the optimizer runs, the name of main can now contain lowercase letters, _, and -, as the names of the lazy chunks already do.
First cause: the debug ID
The builder adds the debug ID line after esbuild picks the file name. If the builder computes the ID in a new way, the bytes of each file change and its name stays the same. The release that fixes this bug would therefore cause the same SRI failure one more time. It would affect apps that use script source maps and SRI and still have the old files in a cache.
I see these options:
Compute the debug ID from the file name and the JS. A later rewrite of the source map can no longer change the JS. The upgrade still breaks the rule once, so the release would need a note that tells users to purge cached JS.
Do option 1 and force a one-time rename. This needs a version comment in the esbuild bundles and a constant in augmentChunkHash. The upgrade is safe, but the bundles carry a comment that exists only to change a hash. I don't like this option.
Keep the current debug ID and name the files from their final bytes. This is the option I prefer.
Option 3 adds a step after injectDebugIds, only when bundles are hashed and script source maps are on. The step hashes each JS file with the names of the other output files replaced by a placeholder. It includes the hashes of the files that the JS file can reach through imports, as Rollup does to handle import cycles. It then renames the files and updates those names in the JS and the source maps. initialFiles and the metafile get the new names, so the index HTML, SRI, the SSR manifest, and the service worker use them.
With option 3, every file with a debug ID gets a new name once, on the upgrade. After that, any change to the bytes of those files changes the name, whatever the cause. The cost is more code, because the builder would rewrite file names inside generated code. Each locale also needs its own copy of initialFiles and the metafile, and the build stats table must print the final names.
Keeping the current debug ID without option 3 avoids the upgrade problem, but the bug comes back the next time the builder writes the source map differently.
Would you accept a rename step like option 3 in the application builder? I have not built it yet. If you would not, which other option do you prefer? I can open a PR for the second cause now and a PR for the first cause once you pick a direction.
Activity
[-]Debug ID changes the bytes of main and polyfills but not their file names, so cached files fail the SRI check[/-][+]Debug ID changes the bytes of polyfills and main but not their file names, so cached files fail the SRI check[/+]jsaguet commentedon Nov 8, 2026
The same symptom has a second cause: the entry file of the chunk optimizer. The debug ID fix above does not fix it.
The chunk optimizer runs when an app has 3 or more lazy chunks. It renames the lazy chunks, and
mainimports them by name. But the optimizer passeschunkFileNamesand noentryFileNames, somainkeeps the name that esbuild gave it.So when a builder release changes how the optimizer names chunks, the bytes of
mainchange and its name stays the same. 22.2.6 did that with #34583.Both builds write
main-5H54S2WB.js, and the twoindex.htmlfiles pin different hashes for it. The only difference in the file is the names of the chunks it imports:The debug ID and the source map of
mainare the same in both builds.A possible fix is to give the entry a content hash with
entryFileNames, the same way aschunkFileNames, and to updateindex.htmlwith the new name. A fix that computes every file name from its final bytes would cover both causes.jsaguet commentedon Nov 8, 2026
I looked at how to fix both causes. The fix for the second cause is small. The fix for the first cause has a trade-off that a maintainer should decide.
The rule I want to keep is that when the bytes of a hashed file change, its name changes too. That rule must also hold on the upgrade to the release that contains the fix.
Second cause: the entry file of the chunk optimizer
The fix passes
entryFileNamesto the threegenerate()calls inchunk-optimizer.ts, somaingets a content hash when bundles are hashed.initialFilesthen uses the new name. WithoutputHashing: none, the file staysmain.js.When the optimizer runs, the name of
maincan now contain lowercase letters,_, and-, as the names of the lazy chunks already do.First cause: the debug ID
The builder adds the debug ID line after esbuild picks the file name. If the builder computes the ID in a new way, the bytes of each file change and its name stays the same. The release that fixes this bug would therefore cause the same SRI failure one more time. It would affect apps that use script source maps and SRI and still have the old files in a cache.
I see these options:
augmentChunkHash. The upgrade is safe, but the bundles carry a comment that exists only to change a hash. I don't like this option.Option 3 adds a step after
injectDebugIds, only when bundles are hashed and script source maps are on. The step hashes each JS file with the names of the other output files replaced by a placeholder. It includes the hashes of the files that the JS file can reach through imports, as Rollup does to handle import cycles. It then renames the files and updates those names in the JS and the source maps.initialFilesand the metafile get the new names, so the index HTML, SRI, the SSR manifest, and the service worker use them.With option 3, every file with a debug ID gets a new name once, on the upgrade. After that, any change to the bytes of those files changes the name, whatever the cause. The cost is more code, because the builder would rewrite file names inside generated code. Each locale also needs its own copy of
initialFilesand the metafile, and the build stats table must print the final names.Keeping the current debug ID without option 3 avoids the upgrade problem, but the bug comes back the next time the builder writes the source map differently.
Would you accept a rename step like option 3 in the application builder? I have not built it yet. If you would not, which other option do you prefer? I can open a PR for the second cause now and a PR for the first cause once you pick a direction.