Skip to content

Commit b995379

Browse files
committed
feat: add 3 new error entries (caching-artifacts x1, runner-environment x1, silent-failures x1)
1 parent 791a27d commit b995379

3 files changed

Lines changed: 452 additions & 0 deletions

File tree

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
id: ca-132
2+
title: '`actions/cache` perpetual miss when GitHub-hosted runner (has zstd) saves cache
3+
and self-hosted runner (no zstd) tries to restore — compression-method suffix
4+
causes different SHA-256 key hash'
5+
category: caching-artifacts
6+
severity: silent-failure
7+
tags:
8+
- actions-cache
9+
- zstd
10+
- gzip
11+
- compression-method
12+
- self-hosted
13+
- cache-miss
14+
- cache-key
15+
- sha256
16+
patterns:
17+
- regex: 'Cache not found for input keys.*self.hosted|self.hosted.*Cache not found'
18+
flags: 'i'
19+
- regex: 'zstd.*not.*found|zstd.*not.*available|zstd.*command.*not.*found'
20+
flags: 'i'
21+
- regex: 'Warning: Cache restore failed'
22+
flags: 'i'
23+
error_messages:
24+
- "Cache not found for input keys: my-cache-key-abc123"
25+
- "Warning: Cache restore failed."
26+
- "zstd: command not found"
27+
root_cause: |
28+
`actions/cache` (all versions) includes the **compression method** as a suffix
29+
component when computing the final SHA-256 hash of the cache key. The compression
30+
method used depends on whether `zstd` is installed on the runner:
31+
32+
```
33+
zstd available?
34+
├── yes → CompressionMethod = "zstd-without-long"
35+
└── no → CompressionMethod = "gzip"
36+
```
37+
38+
The SHA-256 hash is computed over:
39+
`paths | compressionMethod | key`
40+
41+
So the **same explicit `key:` value** produces **two different SHA-256 hashes**
42+
depending on the runner:
43+
44+
| Runner | Components hashed | Final hash |
45+
|--------------------|-------------------------------------------|------------|
46+
| GitHub-hosted (has zstd) | `paths + "zstd-without-long" + key` → SHA256 | hash A |
47+
| Self-hosted (no zstd) | `paths + "gzip" + key` → SHA256 | hash B |
48+
49+
When a GitHub-hosted runner saves a cache entry under hash A, a self-hosted
50+
runner that lacks `zstd` will compute hash B and find no entry — even though
51+
the user-visible `key:` value matches exactly and both runners point to the
52+
**same `ACTIONS_CACHE_URL` cache backend**.
53+
54+
This is distinct from the cross-backend issue (see ca-075) where the cache URL
55+
endpoints differ. This issue occurs even when both runners share the same
56+
GitHub-hosted cache backend — the mismatch is purely in the key hash.
57+
58+
Commonly encountered when:
59+
1. A GitHub-hosted runner saves a warm cache once a week.
60+
2. Self-hosted runners (e.g., bare-metal Linux, custom container, ARC pods
61+
without zstd) try to restore and always get a miss.
62+
3. The explicit `key:` matches in the workflow YAML, so the developer cannot
63+
see why the cache is never found.
64+
fix: |
65+
**Option 1 (Recommended) — Install `zstd` on self-hosted runners:**
66+
Ensure `zstd` is available on all self-hosted runners that share a cache with
67+
GitHub-hosted runners. This makes both runners compute the same
68+
`zstd-without-long` suffix and thus the same SHA-256 hash.
69+
70+
Ubuntu/Debian: `sudo apt-get install -y zstd`
71+
CentOS/RHEL: `sudo yum install -y zstd`
72+
macOS: `brew install zstd`
73+
Windows: `choco install zstandard` or `winget install Facebook.Zstandard`
74+
75+
**Option 2 — Use separate keys for each runner type:**
76+
Explicitly add the compression method (or runner type) to the cache key so
77+
GitHub-hosted and self-hosted entries never collide or get confused:
78+
79+
```yaml
80+
key: myapp-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/lock') }}
81+
```
82+
83+
**Option 3 — Pin all cache-sharing jobs to the same runner type:**
84+
If mixing runner types is unavoidable, have the self-hosted runner save its
85+
own cache entry on first miss. Subsequent runs will hit the self-hosted entry.
86+
fix_code:
87+
- language: yaml
88+
label: 'Broken — GitHub-hosted saves cache; self-hosted always misses (no zstd)'
89+
code: |
90+
jobs:
91+
warm-cache:
92+
runs-on: ubuntu-latest # GitHub-hosted — has zstd → key hash A
93+
steps:
94+
- uses: actions/cache@v4
95+
with:
96+
path: ~/.npm
97+
key: npm-${{ hashFiles('**/package-lock.json') }}
98+
- run: npm ci
99+
100+
test:
101+
runs-on: self-hosted # No zstd → computes key hash B → always misses
102+
needs: warm-cache
103+
steps:
104+
- uses: actions/cache@v4
105+
with:
106+
path: ~/.npm
107+
key: npm-${{ hashFiles('**/package-lock.json') }} # looks same, hash differs
108+
109+
- language: yaml
110+
label: 'Fixed Option 1 — install zstd in self-hosted runner startup step'
111+
code: |
112+
jobs:
113+
test:
114+
runs-on: self-hosted
115+
steps:
116+
- name: Ensure zstd is installed
117+
run: |
118+
if ! command -v zstd &>/dev/null; then
119+
sudo apt-get update -qq && sudo apt-get install -y zstd
120+
fi
121+
122+
- uses: actions/cache@v4
123+
with:
124+
path: ~/.npm
125+
key: npm-${{ hashFiles('**/package-lock.json') }}
126+
# ✓ zstd now available → same hash A as GitHub-hosted runner
127+
128+
- language: yaml
129+
label: 'Fixed Option 2 — include runner type in key to keep entries separate'
130+
code: |
131+
jobs:
132+
test:
133+
runs-on: self-hosted
134+
steps:
135+
- uses: actions/cache@v4
136+
with:
137+
path: ~/.npm
138+
# Include runner identifier so GitHub-hosted and self-hosted
139+
# entries are stored under different keys intentionally:
140+
key: npm-selfhosted-${{ hashFiles('**/package-lock.json') }}
141+
restore-keys: |
142+
npm-selfhosted-
143+
144+
prevention:
145+
- 'Install `zstd` on every self-hosted runner that shares a cache backend with
146+
GitHub-hosted runners — mismatched compression methods produce different key
147+
hashes even when the user-visible `key:` string is identical.'
148+
- 'When diagnosing an unexpected cache miss, run `which zstd` (Linux/macOS) or
149+
`where zstd` (Windows) in the restore job to confirm compression parity.'
150+
- 'Include a runner-type discriminator (e.g. `runner.os`/`runner.arch` or a
151+
custom label) in your `key:` when intentionally mixing GitHub-hosted and
152+
self-hosted runners, so cache entries never silently diverge.'
153+
- 'Set `ACTIONS_RUNNER_DEBUG=true` to see the full internal key hash in the
154+
cache restore log, which makes the compression-method suffix visible for
155+
debugging.'
156+
docs:
157+
- url: 'https://github.com/actions/cache/issues/1755'
158+
label: 'actions/cache#1755: Cache saved by GitHub-hosted runner not found from
159+
self-hosted runner — zstd vs gzip compression key mismatch (May 2026)'
160+
- url: 'https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows'
161+
label: 'GitHub Docs: Caching dependencies to speed up workflows'
162+
- url: 'https://github.com/actions/cache#inputs'
163+
label: 'actions/cache README: key input and restore-keys'
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
id: re-435
2+
title: 'MSB3644: .NETFramework v4.0/v4.5/v4.5.1/v4.6.1 reference assemblies not
3+
found after migrating from windows-2019 to windows-2022/2025 runner'
4+
category: runner-environment
5+
severity: error
6+
tags:
7+
- windows
8+
- dotnet
9+
- msbuild
10+
- netframework
11+
- targeting-pack
12+
- windows-2022
13+
- windows-2025
14+
- windows-2019
15+
- msb3644
16+
patterns:
17+
- regex: 'MSB3644'
18+
flags: 'i'
19+
- regex: 'reference assemblies.*\.NETFramework.*Version.*v4\.(0|5|5\.1|6\.1).*not found'
20+
flags: 'i'
21+
- regex: 'NETFramework.*v4\.(0|5|5\.1|6\.1).*reference assemblies.*not found'
22+
flags: 'i'
23+
- regex: 'install the Developer Pack.*SDK.*Targeting Pack.*this framework'
24+
flags: 'i'
25+
error_messages:
26+
- "Error MSB3644: The reference assemblies for .NETFramework,Version=v4.0 were not
27+
found. To resolve this, install the Developer Pack (SDK/Targeting Pack) for this
28+
framework version or retarget your application."
29+
- "Error MSB3644: The reference assemblies for .NETFramework,Version=v4.5 were not
30+
found."
31+
- "Error MSB3644: The reference assemblies for .NETFramework,Version=v4.5.1 were
32+
not found."
33+
- "Error MSB3644: The reference assemblies for .NETFramework,Version=v4.6.1 were
34+
not found."
35+
root_cause: |
36+
The `windows-2019` GitHub Actions runner shipped with Visual Studio 2019, which
37+
included targeting packs for legacy .NET Framework versions (4.0, 4.5, 4.5.1,
38+
4.6.1). When those targeting packs are installed, MSBuild can resolve reference
39+
assemblies for projects targeting those framework versions.
40+
41+
The `windows-2022` and `windows-2025` runners ship with Visual Studio 2022 or
42+
2026, which does **not** include targeting packs for .NET Framework 4.0, 4.5,
43+
4.5.1, or 4.6.1. The full list of packs **removed** between VS 2019 and VS 2022:
44+
45+
- `Microsoft.Net.Component.4.TargetingPack` (.NET 4.0) — removed
46+
- `Microsoft.Net.Component.4.5.TargetingPack` (.NET 4.5) — removed
47+
- `Microsoft.Net.Component.4.5.1.TargetingPack` (.NET 4.5.1) — removed
48+
- `Microsoft.Net.Component.4.6.1.TargetingPack` (.NET 4.6.1) — removed
49+
50+
.NET 4.5.2, 4.6, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8 targeting packs ARE present
51+
in VS 2022/2026 (windows-2022 and windows-2025 runners).
52+
53+
This error surfaces most often when migrating legacy projects such as SSRS
54+
(SQL Server Reporting Services) reports, older WinForms/WPF applications, or
55+
Azure DevOps extensions that target .NET 4.0 or 4.5.x.
56+
57+
The error is hard to diagnose because `windows-latest` was previously pointing
58+
to `windows-2019` (with the packs), and the migration to `windows-2022` runner
59+
happened silently.
60+
fix: |
61+
**Option 1 (Recommended) — Retarget to .NET 4.5.2 or higher:**
62+
Update the `<TargetFrameworkVersion>` in your `.csproj` / `.vbproj` files to
63+
target `v4.5.2` or higher (e.g., `v4.8`), which are present on all modern
64+
Windows runners. This is the official recommendation from Microsoft.
65+
66+
**Option 2 — Install the .NET Framework Developer Pack in the workflow:**
67+
Download and install the missing targeting pack as a workflow step before
68+
running MSBuild. The Developer Packs are available from aka.ms/msbuild/developerpacks.
69+
70+
**Option 3 — Pin to `windows-2019` (temporary):**
71+
Pin your workflow to `runs-on: windows-2019` as a short-term workaround while
72+
you retarget. Note: windows-2019 is deprecated and will be removed — this is
73+
not a permanent solution.
74+
75+
**Option 4 — Use `choco` to install the targeting pack:**
76+
```
77+
choco install netfx-4.0-devpack --yes
78+
```
79+
fix_code:
80+
- language: yaml
81+
label: 'Broken — windows-2022 runner missing .NET 4.0 targeting pack'
82+
code: |
83+
jobs:
84+
build:
85+
runs-on: windows-2022 # VS 2022 does not include .NET 4.0 targeting pack
86+
steps:
87+
- uses: actions/checkout@v4
88+
- name: Build
89+
run: msbuild MyProject.sln /p:Configuration=Release
90+
# → Error MSB3644: The reference assemblies for .NETFramework,Version=v4.0
91+
# were not found.
92+
93+
- language: yaml
94+
label: 'Fixed Option 1 — install Developer Pack before building'
95+
code: |
96+
jobs:
97+
build:
98+
runs-on: windows-2022
99+
steps:
100+
- uses: actions/checkout@v4
101+
- name: Install .NET Framework 4.0 Developer Pack
102+
shell: pwsh
103+
run: |
104+
$ProgressPreference = 'SilentlyContinue'
105+
# Download the .NET Framework 4 Developer Pack
106+
Invoke-WebRequest `
107+
-Uri 'https://download.microsoft.com/download/F/1/2/F12768F8-F422-4462-8A1B-436A2C6C55D9/NDP40-KB2600211-x86-x64.exe' `
108+
-OutFile net40devpack.exe
109+
Start-Process -Wait -ArgumentList '/quiet' net40devpack.exe
110+
- name: Build
111+
run: msbuild MyProject.sln /p:Configuration=Release
112+
113+
- language: yaml
114+
label: 'Fixed Option 2 — install via Chocolatey'
115+
code: |
116+
jobs:
117+
build:
118+
runs-on: windows-2022
119+
steps:
120+
- uses: actions/checkout@v4
121+
- name: Install .NET 4.0 targeting pack via Chocolatey
122+
shell: pwsh
123+
run: choco install netfx-4.0-devpack --yes --no-progress
124+
- name: Build
125+
run: msbuild MyProject.sln /p:Configuration=Release
126+
127+
- language: yaml
128+
label: 'Fixed Option 3 — retarget project to .NET 4.5.2 (available on all runners)'
129+
code: |
130+
# In your .csproj / .vbproj, change:
131+
# <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
132+
# to:
133+
# <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
134+
# .NET 4.5.2+ targeting packs are present on windows-2022 and windows-2025.
135+
jobs:
136+
build:
137+
runs-on: windows-2022 # ✓ .NET 4.5.2 targeting pack is present
138+
steps:
139+
- uses: actions/checkout@v4
140+
- name: Build (retargeted project)
141+
run: msbuild MyProject.sln /p:Configuration=Release
142+
143+
prevention:
144+
- 'Before migrating from `windows-2019` to `windows-2022` or `windows-2025`,
145+
audit all `.csproj` files for `<TargetFrameworkVersion>` values below v4.5.2
146+
and plan retargeting work.'
147+
- 'Search for `<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>` (and v4.5,
148+
v4.5.1, v4.6.1) in your repo before changing the runner image label.'
149+
- 'Use `windows-2022` explicitly rather than `windows-latest` to control when
150+
the runner image upgrade takes effect and avoid surprise breakage.'
151+
- 'Microsoft recommends targeting .NET Framework 4.8 for all new Windows desktop
152+
and web projects — it receives only security fixes but is fully supported.'
153+
docs:
154+
- url: 'https://github.com/actions/runner-images/issues/13502'
155+
label: 'actions/runner-images#13502: MSB3644 .NETFramework v4.0 reference assemblies
156+
not found after windows-2019 → windows-2022 migration'
157+
- url: 'https://aka.ms/msbuild/developerpacks'
158+
label: 'Microsoft: .NET Framework Developer Packs download page'
159+
- url: 'https://learn.microsoft.com/en-us/visualstudio/releases/2022/system-requirements#visual-studio-2022-net-framework-targeting-packs'
160+
label: 'Visual Studio 2022 .NET Framework targeting pack availability'
161+
- url: 'https://github.com/actions/runner-images/blob/main/images/windows/toolsets/toolset-2022.json'
162+
label: 'windows-2022 runner toolset: installed .NET components'

0 commit comments

Comments
 (0)