@htekdev/actions-debugger 1.0.134 → 1.0.136
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/errors/caching-artifacts/caching-artifacts-080.yml +116 -0
- package/errors/runner-environment/runner-environment-249.yml +144 -0
- package/errors/runner-environment/runner-environment-250.yml +125 -0
- package/errors/runner-environment/runner-environment-251.yml +141 -0
- package/errors/yaml-syntax/yaml-syntax-078.yml +125 -0
- package/errors/yaml-syntax/yaml-syntax-079.yml +148 -0
- package/package.json +1 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
id: caching-artifacts-080
|
|
2
|
+
title: 'Windows runner silently exits during `actions/cache` restore — job proceeds without cached content'
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- actions-cache
|
|
7
|
+
- windows
|
|
8
|
+
- silent-failure
|
|
9
|
+
- cache-restore
|
|
10
|
+
- node-crash
|
|
11
|
+
- post-job
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Cache hit for:.*\nPost job cleanup\.'
|
|
14
|
+
flags: 'im'
|
|
15
|
+
- regex: 'Cache hit for:.*\nPost job cleanup\.\s*\nPost job cleanup\.\s*\nCache up-to-date\.'
|
|
16
|
+
flags: 'ims'
|
|
17
|
+
- regex: 'punycode.*DeprecationWarning.*\nPost job cleanup\.'
|
|
18
|
+
flags: 'im'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Cache hit for: <key>"
|
|
21
|
+
- "Post job cleanup."
|
|
22
|
+
- "Cache up-to-date."
|
|
23
|
+
- "(node:1288) [DEP0040] DeprecationWarning: The `punycode` module is deprecated."
|
|
24
|
+
root_cause: |
|
|
25
|
+
On Windows runners (`windows-latest`, `windows-2022`, `windows-2025`), `actions/cache` can
|
|
26
|
+
silently exit immediately after reporting a cache hit, without actually extracting the cached
|
|
27
|
+
content into the workspace.
|
|
28
|
+
|
|
29
|
+
The observable log pattern:
|
|
30
|
+
1. `Cache hit for: <key>` — cache entry is found on the server
|
|
31
|
+
2. Immediately followed by `Post job cleanup.` (out of order — should appear at end of all steps)
|
|
32
|
+
3. `Cache up-to-date.` — the save post-step fires but there is nothing to save
|
|
33
|
+
4. A Node.js `[DEP0040] DeprecationWarning: The punycode module is deprecated` message appears
|
|
34
|
+
|
|
35
|
+
The result: the job continues with an EMPTY workspace as if the cache was never restored.
|
|
36
|
+
No error is thrown, the step exit code is 0, and subsequent build steps fail with "file not
|
|
37
|
+
found" errors that appear unrelated to caching.
|
|
38
|
+
|
|
39
|
+
Root cause: The `actions/cache` Node.js process running in the runner encounters an unhandled
|
|
40
|
+
internal rejection during archive extraction on Windows and exits silently. The `punycode`
|
|
41
|
+
deprecation warning appearing alongside the premature `Post job cleanup` is a telltale signature
|
|
42
|
+
of this failure mode — it indicates the Node.js process used for the cache action is crashing
|
|
43
|
+
before extraction completes. This pattern is observed sporadically on all Windows runner images
|
|
44
|
+
and correlates with periods of elevated artifact service latency or specific image versions.
|
|
45
|
+
|
|
46
|
+
Reported by multiple projects (Rust/Cargo workflows, f3d, RustPython, linktime) as a roughly
|
|
47
|
+
5% failure rate increase on Windows beginning May 2026.
|
|
48
|
+
fix: |
|
|
49
|
+
1. **Add `continue-on-error: true` as an immediate workaround** — this prevents the workflow
|
|
50
|
+
from failing hard on the cache step. Your build will be slower (no cache hit) but will not
|
|
51
|
+
fail.
|
|
52
|
+
|
|
53
|
+
2. **Re-run the failed job** — the failure is transient and intermittent. Most re-runs succeed.
|
|
54
|
+
|
|
55
|
+
3. **Upgrade `actions/cache` to the latest version** — the `actions/cache@v5` release line
|
|
56
|
+
includes improved error handling for Windows. Pin to `v5.4.0` or later.
|
|
57
|
+
|
|
58
|
+
4. **Use `save-always: false` and add a diagnostic step** — log the cache extraction result
|
|
59
|
+
explicitly to detect silent failures:
|
|
60
|
+
```yaml
|
|
61
|
+
- run: |
|
|
62
|
+
if (-not (Test-Path "expected-file-from-cache")) {
|
|
63
|
+
Write-Host "##[warning]Cache may not have been restored (file missing)"
|
|
64
|
+
}
|
|
65
|
+
shell: pwsh
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
5. **Split cache and build into separate jobs** — this forces a fresh runner for the build
|
|
69
|
+
job and avoids the Windows Node.js process issue affecting the current runner slot.
|
|
70
|
+
fix_code:
|
|
71
|
+
- language: yaml
|
|
72
|
+
label: 'Immediate workaround — add continue-on-error to cache step'
|
|
73
|
+
code: |
|
|
74
|
+
- name: Restore Cargo cache
|
|
75
|
+
id: cache-cargo
|
|
76
|
+
uses: actions/cache@v5
|
|
77
|
+
continue-on-error: true # ← prevents silent exit from blocking the job
|
|
78
|
+
with:
|
|
79
|
+
path: |
|
|
80
|
+
~/.cargo/registry/index/
|
|
81
|
+
~/.cargo/registry/cache/
|
|
82
|
+
~/.cargo/git/db/
|
|
83
|
+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
84
|
+
restore-keys: |
|
|
85
|
+
${{ runner.os }}-cargo-
|
|
86
|
+
|
|
87
|
+
- name: Verify cache restoration
|
|
88
|
+
shell: pwsh
|
|
89
|
+
run: |
|
|
90
|
+
if (Test-Path "$env:USERPROFILE\.cargo\registry\index") {
|
|
91
|
+
Write-Host "Cache restored successfully"
|
|
92
|
+
} else {
|
|
93
|
+
Write-Host "##[warning]Cache not restored — building from scratch"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
- language: yaml
|
|
97
|
+
label: 'Use actions/cache@v5 with latest patch version'
|
|
98
|
+
code: |
|
|
99
|
+
- uses: actions/cache@v5 # v5.4.0+ has improved Windows extraction error handling
|
|
100
|
+
with:
|
|
101
|
+
path: ~/.cargo/registry
|
|
102
|
+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
103
|
+
restore-keys: ${{ runner.os }}-cargo-
|
|
104
|
+
prevention:
|
|
105
|
+
- 'Always pin `actions/cache` to a recent v5.x release — older versions have known Windows Node.js crash issues'
|
|
106
|
+
- 'Add `continue-on-error: true` to cache restore steps to prevent silent failures from blocking CI'
|
|
107
|
+
- 'Add a post-cache verification step on Windows to confirm expected files exist before running the build'
|
|
108
|
+
- 'Monitor Windows job failure rates — a sudden 3-5% increase often signals a cache extraction regression'
|
|
109
|
+
- 'Use `ACTIONS_RUNNER_DEBUG=true` in repository secrets to surface the Node.js exit code diagnostic line'
|
|
110
|
+
docs:
|
|
111
|
+
- url: 'https://github.com/actions/cache/issues/1754'
|
|
112
|
+
label: 'actions/cache#1754 — Windows runner randomly dies in actions/cache (May 2026)'
|
|
113
|
+
- url: 'https://github.com/actions/cache'
|
|
114
|
+
label: 'actions/cache — GitHub Actions caching documentation'
|
|
115
|
+
- url: 'https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows'
|
|
116
|
+
label: 'GitHub Docs — Caching dependencies to speed up workflows'
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
id: runner-environment-249
|
|
2
|
+
title: '`actions/github-script@v9` breaking changes — `const getOctokit` SyntaxError + `require(''@actions/github'')` ERR_REQUIRE_ESM'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- github-script
|
|
7
|
+
- v9
|
|
8
|
+
- breaking-change
|
|
9
|
+
- getOctokit
|
|
10
|
+
- esm
|
|
11
|
+
- require
|
|
12
|
+
- SyntaxError
|
|
13
|
+
- octokit
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: 'SyntaxError.*Identifier.*getOctokit.*already been declared'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: "require.*@actions/github.*ERR_REQUIRE_ESM|ERR_REQUIRE_ESM.*@actions/github"
|
|
18
|
+
flags: 'i'
|
|
19
|
+
- regex: 'github-script.*v9.*const getOctokit|const getOctokit.*github-script.*v9'
|
|
20
|
+
flags: 'i'
|
|
21
|
+
- regex: "require.*@actions/github.*ES Module.*not supported"
|
|
22
|
+
flags: 'i'
|
|
23
|
+
error_messages:
|
|
24
|
+
- "SyntaxError: Identifier 'getOctokit' has already been declared"
|
|
25
|
+
- "Error [ERR_REQUIRE_ESM]: require() of ES Module /node_modules/@actions/github/lib/github.js is not supported."
|
|
26
|
+
- "Instead change the require of index.js to a dynamic import() which is available in all CommonJS modules."
|
|
27
|
+
root_cause: |
|
|
28
|
+
`actions/github-script@v9.0.0` (released April 9, 2026) introduced two breaking changes
|
|
29
|
+
compared to v8 and earlier:
|
|
30
|
+
|
|
31
|
+
BREAKING CHANGE 1: `getOctokit` is now an injected function parameter
|
|
32
|
+
In v9, the `getOctokit` factory function is injected directly into the script's
|
|
33
|
+
execution context as a named function parameter (alongside `github`, `context`, `core`,
|
|
34
|
+
etc.). This means `getOctokit` is now a declared parameter name in the script scope.
|
|
35
|
+
|
|
36
|
+
If a script re-declares `getOctokit` using `const` or `let`, JavaScript throws a
|
|
37
|
+
SyntaxError at parse time because re-declaring a `const`/`let` binding that already
|
|
38
|
+
exists in scope is illegal:
|
|
39
|
+
|
|
40
|
+
const getOctokit = require('@octokit/rest').Octokit // ❌ SyntaxError in v9
|
|
41
|
+
let getOctokit = ... // ❌ SyntaxError in v9
|
|
42
|
+
|
|
43
|
+
Using `var` avoids this issue because `var` allows redeclaration in the same scope
|
|
44
|
+
(though the injected value will be shadowed).
|
|
45
|
+
|
|
46
|
+
BREAKING CHANGE 2: `require('@actions/github')` no longer works
|
|
47
|
+
`@actions/github` v9 is an ESM-only package. The `github-script` execution context
|
|
48
|
+
is CommonJS (CJS). Calling `require('@actions/github')` from within a script will
|
|
49
|
+
fail at runtime:
|
|
50
|
+
|
|
51
|
+
const { getOctokit } = require('@actions/github') // ❌ ERR_REQUIRE_ESM in v9
|
|
52
|
+
|
|
53
|
+
This pattern was previously used in v7/v8 scripts to create secondary Octokit
|
|
54
|
+
clients with custom tokens. In v9, this is replaced by the injected `getOctokit`
|
|
55
|
+
function parameter, which eliminates the need to require `@actions/github` at all.
|
|
56
|
+
|
|
57
|
+
Impact: Scripts that used `require('@actions/github')` to create custom clients
|
|
58
|
+
(e.g., for cross-organization access or GitHub App tokens) will fail when the
|
|
59
|
+
action is upgraded or when `@latest` resolves to v9.
|
|
60
|
+
fix: |
|
|
61
|
+
FIX FOR BREAKING CHANGE 1 (SyntaxError: 'getOctokit' already declared):
|
|
62
|
+
Remove or rename the local `getOctokit` variable declaration. The injected
|
|
63
|
+
`getOctokit` is available directly in the script scope without any import.
|
|
64
|
+
If you need to keep the variable name, use `var` instead of `const`/`let` — or
|
|
65
|
+
simply rename the local variable to something else.
|
|
66
|
+
|
|
67
|
+
FIX FOR BREAKING CHANGE 2 (ERR_REQUIRE_ESM from require('@actions/github')):
|
|
68
|
+
Replace the `require('@actions/github')` pattern with the injected `getOctokit`
|
|
69
|
+
function. In v9, `getOctokit(token)` is available directly in the script context
|
|
70
|
+
and accepts a PAT or GitHub App token to create a secondary authenticated client.
|
|
71
|
+
|
|
72
|
+
General recommendation: pin to `actions/github-script@v9` explicitly and
|
|
73
|
+
migrate your scripts using the patterns below.
|
|
74
|
+
fix_code:
|
|
75
|
+
- language: yaml
|
|
76
|
+
label: 'Fix SyntaxError — use injected getOctokit directly, remove const/let redeclaration'
|
|
77
|
+
code: |
|
|
78
|
+
- uses: actions/github-script@v9
|
|
79
|
+
with:
|
|
80
|
+
script: |
|
|
81
|
+
# ❌ v8 and earlier — required declaration to get getOctokit:
|
|
82
|
+
# const { getOctokit } = require('@actions/github')
|
|
83
|
+
# const myOctokit = getOctokit(process.env.MY_TOKEN)
|
|
84
|
+
|
|
85
|
+
# ❌ v9 SyntaxError — can't redeclare the injected getOctokit parameter:
|
|
86
|
+
# const getOctokit = require('@actions/github').getOctokit // SyntaxError!
|
|
87
|
+
|
|
88
|
+
# ✅ v9 — getOctokit is injected directly; use it without any import:
|
|
89
|
+
const myOctokit = getOctokit(process.env.MY_TOKEN)
|
|
90
|
+
const result = await myOctokit.rest.repos.get({
|
|
91
|
+
owner: 'my-org',
|
|
92
|
+
repo: 'my-private-repo',
|
|
93
|
+
})
|
|
94
|
+
core.info(`Repo: ${result.data.full_name}`)
|
|
95
|
+
env:
|
|
96
|
+
MY_TOKEN: ${{ secrets.MY_CROSS_REPO_TOKEN }}
|
|
97
|
+
|
|
98
|
+
- language: yaml
|
|
99
|
+
label: 'Fix ERR_REQUIRE_ESM — replace require(''@actions/github'') with injected getOctokit'
|
|
100
|
+
code: |
|
|
101
|
+
- uses: actions/github-script@v9
|
|
102
|
+
with:
|
|
103
|
+
script: |
|
|
104
|
+
# ❌ v8 pattern — fails with ERR_REQUIRE_ESM in v9:
|
|
105
|
+
# const { getOctokit } = require('@actions/github')
|
|
106
|
+
# const crossOrgClient = getOctokit(process.env.CROSS_ORG_TOKEN)
|
|
107
|
+
|
|
108
|
+
# ✅ v9 pattern — use injected getOctokit directly:
|
|
109
|
+
const crossOrgClient = getOctokit(process.env.CROSS_ORG_TOKEN)
|
|
110
|
+
await crossOrgClient.rest.issues.create({
|
|
111
|
+
owner: 'other-org',
|
|
112
|
+
repo: 'other-repo',
|
|
113
|
+
title: 'Cross-org issue from Actions',
|
|
114
|
+
body: 'Created via github-script v9 getOctokit factory'
|
|
115
|
+
})
|
|
116
|
+
env:
|
|
117
|
+
CROSS_ORG_TOKEN: ${{ secrets.CROSS_ORG_PAT }}
|
|
118
|
+
|
|
119
|
+
- language: yaml
|
|
120
|
+
label: 'Use dynamic import() as alternative for @actions/github internals'
|
|
121
|
+
code: |
|
|
122
|
+
- uses: actions/github-script@v9
|
|
123
|
+
with:
|
|
124
|
+
script: |
|
|
125
|
+
# If you need @actions/github internals beyond getOctokit/github,
|
|
126
|
+
# use dynamic import() — ESM packages can be imported this way in github-script:
|
|
127
|
+
const actionsGithub = await import('@actions/github')
|
|
128
|
+
const customClient = actionsGithub.getOctokit(process.env.MY_TOKEN)
|
|
129
|
+
core.info('Loaded via dynamic import')
|
|
130
|
+
env:
|
|
131
|
+
MY_TOKEN: ${{ secrets.MY_TOKEN }}
|
|
132
|
+
prevention:
|
|
133
|
+
- "Pin `uses: actions/github-script@v9` (not `@latest`) to control when you upgrade and avoid surprise breaking changes"
|
|
134
|
+
- "Remove all `require('@actions/github')` calls from github-script scripts — use the injected `getOctokit` function parameter instead"
|
|
135
|
+
- "Never re-declare `getOctokit`, `github`, `context`, `core`, `exec`, `glob`, `io`, `fetch`, or `require` with `const`/`let` — these are all injected parameters in v9"
|
|
136
|
+
- "When creating secondary Octokit clients, use `getOctokit(token)` directly without any imports — this pattern works in all versions of github-script that inject the factory"
|
|
137
|
+
- "Review the v9.0.0 release notes when upgrading from v8: https://github.com/actions/github-script/releases/tag/v9.0.0"
|
|
138
|
+
docs:
|
|
139
|
+
- url: 'https://github.com/actions/github-script/releases/tag/v9.0.0'
|
|
140
|
+
label: 'actions/github-script v9.0.0 release notes — breaking changes (April 9, 2026)'
|
|
141
|
+
- url: 'https://github.com/actions/github-script#creating-additional-clients-with-getoctokit'
|
|
142
|
+
label: 'actions/github-script README — Creating additional clients with getOctokit (v9 pattern)'
|
|
143
|
+
- url: 'https://github.com/actions/github-script/pull/700'
|
|
144
|
+
label: 'actions/github-script PR#700 — add getOctokit to script context, upgrade @actions/github v9'
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
id: runner-environment-250
|
|
2
|
+
title: '`dotnet-version: 10.0.x` resolves to non-existent patch version — aka.ms redirect misconfigured during .NET release cycle'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- setup-dotnet
|
|
7
|
+
- dotnet
|
|
8
|
+
- version-not-found
|
|
9
|
+
- aka-ms-redirect
|
|
10
|
+
- install-scripts
|
|
11
|
+
- wildcard-version
|
|
12
|
+
- net10
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'Could not find \.NET Core (SDK|Runtime) with version = \d+\.\d+\.\d+'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'dotnet-install: The resource at aka\.ms link.*is not available'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'Failed to install dotnet.*exit code: 1.*Could not find.*version = \d+\.\d+\.\d+'
|
|
19
|
+
flags: 'ims'
|
|
20
|
+
- regex: 'Attempting to download using aka\.ms link.*dotnet-(sdk|runtime)-\d+\.\d+\.\d+-.*\.tar\.gz.*404'
|
|
21
|
+
flags: 'ims'
|
|
22
|
+
error_messages:
|
|
23
|
+
- "dotnet_install: Error: Could not find `.NET Core SDK` with version = 10.0.201"
|
|
24
|
+
- "dotnet_install: Error: Could not find `.NET Core Runtime` with version = 10.0.5"
|
|
25
|
+
- "dotnet-install: The resource at aka.ms link 'https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.201/dotnet-sdk-10.0.201-linux-x64.tar.gz' is not available."
|
|
26
|
+
- "Error: Failed to install dotnet, exit code: 1."
|
|
27
|
+
- "curl: (22) The requested URL returned error: 404 Not Found"
|
|
28
|
+
root_cause: |
|
|
29
|
+
`actions/setup-dotnet` and the underlying `dotnet/install-scripts` use `aka.ms` redirect
|
|
30
|
+
URLs maintained by Microsoft to resolve the "latest" version for a given channel (e.g.,
|
|
31
|
+
`10.0.x`, `LTS`, `STS`). These redirects point to a specific patch version build URL.
|
|
32
|
+
|
|
33
|
+
When Microsoft publishes a new .NET patch version, the `aka.ms` redirects are briefly updated
|
|
34
|
+
to point to the new version's build artifacts. If the redirect is updated BEFORE the actual
|
|
35
|
+
build artifacts are published to `builds.dotnet.microsoft.com`, every workflow using a wildcard
|
|
36
|
+
version spec (e.g., `dotnet-version: 10.0.x` or `dotnet-version: LTS`) will attempt to
|
|
37
|
+
download a version that doesn't exist yet, receiving a 404.
|
|
38
|
+
|
|
39
|
+
This race condition between the redirect update and the artifact publication means that all
|
|
40
|
+
GitHub Actions workflows using `actions/setup-dotnet` with wildcard versions simultaneously
|
|
41
|
+
fail for a period of 15 minutes to several hours until the artifacts are published.
|
|
42
|
+
|
|
43
|
+
The pattern recurred:
|
|
44
|
+
- **March 10, 2026**: `LTS` redirect pointed to `10.0.5` / SDK `10.0.201` before they existed
|
|
45
|
+
- **May 16, 2026**: Same issue returned, confirmed by multiple users
|
|
46
|
+
|
|
47
|
+
The `dotnet-install.sh` script constructs the download URL directly from the version number
|
|
48
|
+
returned by the `aka.ms` redirect, so there is no fallback to the previous known-good version.
|
|
49
|
+
|
|
50
|
+
Note: This is distinct from the `.NET 6/7 EOL` version-not-found issue (`runner-environment-166`),
|
|
51
|
+
which is caused by Microsoft permanently removing old build artifacts. This issue is a transient
|
|
52
|
+
race condition during new-version rollout and typically self-resolves within hours.
|
|
53
|
+
fix: |
|
|
54
|
+
**Immediate action**: Re-run failed workflow jobs — the issue is transient and self-resolves
|
|
55
|
+
once Microsoft finishes publishing the new .NET artifacts (usually within 1-4 hours).
|
|
56
|
+
|
|
57
|
+
**Pin to an exact version** to avoid hitting the redirect race condition:
|
|
58
|
+
Use `dotnet-version: '10.0.4'` (or whichever the last confirmed-working patch is) instead of
|
|
59
|
+
`dotnet-version: '10.0.x'`. Check https://dotnet.microsoft.com/en-us/download/dotnet/10.0 for
|
|
60
|
+
the latest stable version.
|
|
61
|
+
|
|
62
|
+
**Use the global.json approach** with a `rollForward` policy instead of the wildcard spec:
|
|
63
|
+
Create a `global.json` file with a safe `latestPatch` roll-forward policy that lets the
|
|
64
|
+
installed SDK satisfy your version requirement without fetching a specific patch.
|
|
65
|
+
|
|
66
|
+
**File an issue at dotnet/core** if the outage persists — the root cause is in the aka.ms
|
|
67
|
+
redirect management maintained by the .NET team at Microsoft.
|
|
68
|
+
fix_code:
|
|
69
|
+
- language: yaml
|
|
70
|
+
label: 'Pin to an exact .NET 10 version instead of wildcard to avoid redirect race'
|
|
71
|
+
code: |
|
|
72
|
+
- uses: actions/setup-dotnet@v4
|
|
73
|
+
with:
|
|
74
|
+
# Exact version — immune to aka.ms redirect race conditions
|
|
75
|
+
dotnet-version: '10.0.4'
|
|
76
|
+
# Or use global.json for project-level version pinning:
|
|
77
|
+
# dotnet-version-file: 'global.json'
|
|
78
|
+
|
|
79
|
+
- language: yaml
|
|
80
|
+
label: 'Use global.json with latestPatch roll-forward for safer version resolution'
|
|
81
|
+
code: |
|
|
82
|
+
# global.json at repo root:
|
|
83
|
+
# {
|
|
84
|
+
# "sdk": {
|
|
85
|
+
# "version": "10.0.100",
|
|
86
|
+
# "rollForward": "latestPatch"
|
|
87
|
+
# }
|
|
88
|
+
# }
|
|
89
|
+
|
|
90
|
+
# In your workflow:
|
|
91
|
+
- uses: actions/setup-dotnet@v4
|
|
92
|
+
with:
|
|
93
|
+
dotnet-version-file: 'global.json' # resolves from the file, not aka.ms
|
|
94
|
+
|
|
95
|
+
- language: yaml
|
|
96
|
+
label: 'Add retry logic with continue-on-error + fallback to cached SDK'
|
|
97
|
+
code: |
|
|
98
|
+
- name: Setup .NET (with retry)
|
|
99
|
+
id: setup-dotnet
|
|
100
|
+
uses: actions/setup-dotnet@v4
|
|
101
|
+
continue-on-error: true
|
|
102
|
+
with:
|
|
103
|
+
dotnet-version: '10.0.x'
|
|
104
|
+
|
|
105
|
+
- name: Fallback — install pinned .NET if wildcard failed
|
|
106
|
+
if: steps.setup-dotnet.outcome == 'failure'
|
|
107
|
+
uses: actions/setup-dotnet@v4
|
|
108
|
+
with:
|
|
109
|
+
dotnet-version: '10.0.4' # last known-good version
|
|
110
|
+
|
|
111
|
+
prevention:
|
|
112
|
+
- 'Prefer exact version pins (`10.0.4`) over wildcards (`10.0.x`) in production CI — reduces exposure to aka.ms redirect race conditions'
|
|
113
|
+
- 'Use `dotnet-version-file: global.json` with `rollForward: latestPatch` for project-level SDK pinning that avoids direct aka.ms resolution'
|
|
114
|
+
- 'Monitor https://github.com/dotnet/install-scripts and https://github.com/actions/setup-dotnet for known regressions before major .NET patch days'
|
|
115
|
+
- 'Subscribe to dotnet/core announcements to anticipate .NET patch releases — aka.ms redirects are updated around Patch Tuesday each month'
|
|
116
|
+
- 'Add `continue-on-error: true` + a fallback step to tolerate transient outages without blocking CI entirely'
|
|
117
|
+
docs:
|
|
118
|
+
- url: 'https://github.com/actions/setup-dotnet/issues/711'
|
|
119
|
+
label: 'actions/setup-dotnet#711 — dotnet-version: 10.0.x tries to download non-existent version 10.0.5 (March 2026, 48 reactions)'
|
|
120
|
+
- url: 'https://github.com/dotnet/install-scripts'
|
|
121
|
+
label: 'dotnet/install-scripts — underlying install script that resolves aka.ms redirects'
|
|
122
|
+
- url: 'https://dotnet.microsoft.com/en-us/download/dotnet/10.0'
|
|
123
|
+
label: '.NET 10 download page — latest stable SDK/runtime versions'
|
|
124
|
+
- url: 'https://learn.microsoft.com/en-us/dotnet/core/tools/global-json'
|
|
125
|
+
label: 'global.json overview — SDK version selection with rollForward policies'
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
id: runner-environment-251
|
|
2
|
+
title: '.NET SDK 10.0.200+ requires MSBuild 18.0 but `windows-2022` only ships MSBuild 17 (VS 2022)'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- dotnet
|
|
7
|
+
- msbuild
|
|
8
|
+
- windows-2022
|
|
9
|
+
- net10
|
|
10
|
+
- visual-studio-2022
|
|
11
|
+
- version-incompatibility
|
|
12
|
+
- setup-dotnet
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'Version \d+\.0\.\d{3} of the \.NET SDK requires at least version 18\.0\.0 of MSBuild'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'The current available version of MSBuild is 17\.\d+\.\d+'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'Could not resolve SDK.*Microsoft\.NET\.Sdk.*version 18\.0\.0 of MSBuild'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
- regex: 'MSBuild version.*requires.*18\.0.*current.*17\.'
|
|
21
|
+
flags: 'i'
|
|
22
|
+
error_messages:
|
|
23
|
+
- "Version 10.0.200 of the .NET SDK requires at least version 18.0.0 of MSBuild. The current available version of MSBuild is 17.14.40.60911."
|
|
24
|
+
- "D:\\a\\...\\MyApp.csproj : error : Could not resolve SDK \"Microsoft.NET.Sdk\"."
|
|
25
|
+
- "Change the .NET SDK specified in global.json to an older version that requires the MSBuild version currently available."
|
|
26
|
+
- "error MSB4236: The SDK 'Microsoft.NET.Sdk.Web' specified could not be found."
|
|
27
|
+
root_cause: |
|
|
28
|
+
Starting with .NET SDK **10.0.200** (released around March 2026), the .NET SDK toolchain
|
|
29
|
+
requires **MSBuild 18.0 or later** to build projects. This requirement was introduced because
|
|
30
|
+
.NET SDK 10.0.200 includes MSBuild features that depend on capabilities added in MSBuild 18.
|
|
31
|
+
|
|
32
|
+
The `windows-2022` GitHub Actions runner ships with **Visual Studio 2022**, which provides
|
|
33
|
+
MSBuild 17.x (currently 17.14.x). MSBuild 18.0 is only included in **Visual Studio 2026**
|
|
34
|
+
(VS 18.x), which ships with `windows-2025-vs2026` and `windows-latest` (as of June 2026).
|
|
35
|
+
|
|
36
|
+
Trigger scenario:
|
|
37
|
+
1. Workflow uses `runs-on: windows-2022` (deliberately pinned or using legacy config)
|
|
38
|
+
2. Workflow installs .NET 10 SDK via `actions/setup-dotnet` with `dotnet-version: '10.0.x'`
|
|
39
|
+
or `dotnet-version: '10.x'`
|
|
40
|
+
3. `dotnet build`, `dotnet restore`, or NuGet restore attempts to invoke MSBuild
|
|
41
|
+
4. MSBuild 17.14 is found on PATH from VS 2022 installation
|
|
42
|
+
5. .NET SDK rejects MSBuild 17.x and throws the version incompatibility error
|
|
43
|
+
|
|
44
|
+
Note: This does NOT affect `windows-latest` users as of June 8, 2026, because `windows-latest`
|
|
45
|
+
now resolves to VS 2026 (MSBuild 18.x). The issue is specific to workflows explicitly pinning
|
|
46
|
+
to `windows-2022` (which keeps VS 2022/MSBuild 17.x) while also installing .NET SDK 10.0.200+.
|
|
47
|
+
|
|
48
|
+
NuGet-based restores (e.g., via `NuGetCommand@2` or `nuget.exe`) are also affected because
|
|
49
|
+
NuGet's MSBuild auto-detection picks up the VS 2022 MSBuild binary and .NET SDK 10.0.200+
|
|
50
|
+
then rejects it during SDK resolution.
|
|
51
|
+
fix: |
|
|
52
|
+
**Option 1 — Switch to windows-latest or windows-2025-vs2026** (recommended):
|
|
53
|
+
These runner labels ship with Visual Studio 2026 (MSBuild 18.x), which satisfies the
|
|
54
|
+
.NET SDK 10.0.200+ MSBuild requirement.
|
|
55
|
+
|
|
56
|
+
**Option 2 — Pin to an older .NET SDK that supports MSBuild 17.x**:
|
|
57
|
+
.NET SDK 10.0.100 (the initial .NET 10 release) works with MSBuild 17.x. Pin via `global.json`
|
|
58
|
+
or `dotnet-version: '10.0.100'` if you must stay on `windows-2022`.
|
|
59
|
+
|
|
60
|
+
**Option 3 — Install VS Build Tools 2026 in your workflow**:
|
|
61
|
+
Install the VS 2026 Build Tools workload as a workflow step to get MSBuild 18.x on
|
|
62
|
+
`windows-2022`. This is slow (~5-10 min) but avoids the runner label change.
|
|
63
|
+
|
|
64
|
+
**Option 4 — Use the .NET SDK's bundled MSBuild**:
|
|
65
|
+
When you call `dotnet build` (not `msbuild` directly), .NET SDK 10.0.200+ uses its own
|
|
66
|
+
bundled MSBuild. This bypasses the VS 2022 MSBuild detection. Use `dotnet build` instead
|
|
67
|
+
of `msbuild` to work around the issue on `windows-2022`.
|
|
68
|
+
fix_code:
|
|
69
|
+
- language: yaml
|
|
70
|
+
label: 'Switch to windows-latest (VS 2026, MSBuild 18.x) — simplest fix'
|
|
71
|
+
code: |
|
|
72
|
+
jobs:
|
|
73
|
+
build:
|
|
74
|
+
runs-on: windows-latest # now VS 2026 + MSBuild 18.x as of June 2026
|
|
75
|
+
# was: runs-on: windows-2022 ← VS 2022, MSBuild 17.x — incompatible with .NET 10.0.200+
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
- uses: actions/setup-dotnet@v4
|
|
79
|
+
with:
|
|
80
|
+
dotnet-version: '10.0.x'
|
|
81
|
+
- run: dotnet build
|
|
82
|
+
|
|
83
|
+
- language: yaml
|
|
84
|
+
label: 'Pin .NET SDK to 10.0.100 (MSBuild 17-compatible) if staying on windows-2022'
|
|
85
|
+
code: |
|
|
86
|
+
jobs:
|
|
87
|
+
build:
|
|
88
|
+
runs-on: windows-2022 # VS 2022 / MSBuild 17.x
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v4
|
|
91
|
+
- uses: actions/setup-dotnet@v4
|
|
92
|
+
with:
|
|
93
|
+
# Pin to 10.0.100 — the last .NET 10 SDK version that works with MSBuild 17.x
|
|
94
|
+
dotnet-version: '10.0.100'
|
|
95
|
+
- run: dotnet build # uses dotnet's bundled MSBuild, not VS 2022 MSBuild
|
|
96
|
+
|
|
97
|
+
- language: yaml
|
|
98
|
+
label: 'Use dotnet build (bundled MSBuild) instead of msbuild.exe directly'
|
|
99
|
+
code: |
|
|
100
|
+
jobs:
|
|
101
|
+
build:
|
|
102
|
+
runs-on: windows-2022
|
|
103
|
+
steps:
|
|
104
|
+
- uses: actions/checkout@v4
|
|
105
|
+
- uses: actions/setup-dotnet@v4
|
|
106
|
+
with:
|
|
107
|
+
dotnet-version: '10.0.x'
|
|
108
|
+
# Use 'dotnet build' — uses the SDK's bundled MSBuild (18.x), not VS 2022's MSBuild
|
|
109
|
+
- run: dotnet build MyApp.sln /p:Configuration=Release
|
|
110
|
+
# Avoid: msbuild MyApp.sln — this finds VS 2022's MSBuild 17.x and fails
|
|
111
|
+
|
|
112
|
+
- language: yaml
|
|
113
|
+
label: 'Matrix build — test against both windows-2022 and windows-latest'
|
|
114
|
+
code: |
|
|
115
|
+
jobs:
|
|
116
|
+
build:
|
|
117
|
+
strategy:
|
|
118
|
+
matrix:
|
|
119
|
+
os: [windows-2022, windows-latest]
|
|
120
|
+
runs-on: ${{ matrix.os }}
|
|
121
|
+
steps:
|
|
122
|
+
- uses: actions/checkout@v4
|
|
123
|
+
- uses: actions/setup-dotnet@v4
|
|
124
|
+
with:
|
|
125
|
+
dotnet-version: '10.0.x'
|
|
126
|
+
- run: dotnet build
|
|
127
|
+
prevention:
|
|
128
|
+
- 'Use `windows-latest` for .NET 10+ workflows — as of June 2026 it ships with VS 2026 (MSBuild 18.x)'
|
|
129
|
+
- 'Call `dotnet build` / `dotnet publish` instead of invoking `msbuild.exe` directly — the SDK bundled MSBuild always matches the SDK requirements'
|
|
130
|
+
- 'When pinning to `windows-2022`, also pin the .NET SDK to a version compatible with MSBuild 17.x (10.0.100 or earlier) if you need both'
|
|
131
|
+
- 'Check MSBuild version compatibility before upgrading .NET SDK patch versions: dotnet.microsoft.com/en-us/download/dotnet/10.0'
|
|
132
|
+
- 'Subscribe to actions/runner-images announcements to know when `windows-latest` changes its VS version so you can plan migrations'
|
|
133
|
+
docs:
|
|
134
|
+
- url: 'https://github.com/actions/runner-images/issues/13789'
|
|
135
|
+
label: 'runner-images#13789 — NuGet restore fails with NET 10 starting after March 9, 2026 update'
|
|
136
|
+
- url: 'https://github.com/actions/runner-images/issues/14017'
|
|
137
|
+
label: 'runner-images#14017 — windows-latest and windows-2025 migrate to VS 2026 in June 2026'
|
|
138
|
+
- url: 'https://learn.microsoft.com/en-us/dotnet/core/tools/sdk-errors/netsdk1045'
|
|
139
|
+
label: 'NETSDK1045 — The current .NET SDK does not support targeting .NET X.0'
|
|
140
|
+
- url: 'https://dotnet.microsoft.com/en-us/download/dotnet/10.0'
|
|
141
|
+
label: '.NET 10 download — SDK/Runtime version history'
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
id: yaml-syntax-078
|
|
2
|
+
title: '`vars.*` context returns empty string for undefined configuration variables, causing "unexpected value ''" in reusable workflow with: inputs'
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- vars-context
|
|
7
|
+
- reusable-workflow
|
|
8
|
+
- workflow-call
|
|
9
|
+
- configuration-variables
|
|
10
|
+
- empty-string
|
|
11
|
+
- unexpected-value
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'evaluate reusable workflow inputs.*unexpected value'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'vars\.\w+.*unexpected value|unexpected value.*vars\.\w+'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'unexpected value ''''.*reusable.*input|reusable.*input.*unexpected value '''''
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "evaluate reusable workflow inputs: key 'my-input': unexpected value ''"
|
|
21
|
+
- "evaluate reusable workflow inputs: unexpected value '' for boolean input"
|
|
22
|
+
- "Error: evaluate reusable workflow inputs: ... unexpected value ''"
|
|
23
|
+
root_cause: |
|
|
24
|
+
When a caller workflow passes a `vars.*` context expression to a reusable workflow's
|
|
25
|
+
`with:` input, and that configuration variable is not defined in the repository or
|
|
26
|
+
organization settings, `${{ vars.SOME_VAR }}` evaluates to an empty string `""`.
|
|
27
|
+
|
|
28
|
+
Reusable workflow inputs that are typed as `boolean` or `number` (or have strict
|
|
29
|
+
validation) reject the empty string `""` with the error:
|
|
30
|
+
evaluate reusable workflow inputs: ... unexpected value ''
|
|
31
|
+
|
|
32
|
+
For `type: boolean` inputs, the only valid values are `"true"` or `"false"`.
|
|
33
|
+
An empty string `""` is not a valid boolean and triggers this error.
|
|
34
|
+
For `type: number` inputs, `""` is not parseable as a number.
|
|
35
|
+
|
|
36
|
+
Even for `type: string` inputs with `required: true`, some versions of the runner
|
|
37
|
+
treat `""` as "not provided" and reject it with this error.
|
|
38
|
+
|
|
39
|
+
Root causes of the empty string:
|
|
40
|
+
1. The developer never created the configuration variable in the repository/organization
|
|
41
|
+
Settings → Variables UI. `vars.*` is the context for configuration variables (set
|
|
42
|
+
in the UI), NOT for environment variables set with `env:` in the workflow YAML.
|
|
43
|
+
2. The variable was created at the wrong scope — e.g., created as an environment-level
|
|
44
|
+
variable but the workflow doesn't reference a matching environment.
|
|
45
|
+
3. The variable name was mistyped — `vars.MY_VAR` but the setting is named `MY_VAr`
|
|
46
|
+
(case-sensitive match is required).
|
|
47
|
+
|
|
48
|
+
This is a common confusion: developers sometimes set `env:` workflow-level variables
|
|
49
|
+
expecting them to be accessible via `vars.*`, but `vars.*` only resolves variables
|
|
50
|
+
set in the GitHub repository/organization/environment Settings UI.
|
|
51
|
+
fix: |
|
|
52
|
+
Option 1 — Create the configuration variable in GitHub Settings:
|
|
53
|
+
Go to your repository (or organization/environment) Settings → Secrets and variables
|
|
54
|
+
→ Actions → Variables tab, and create the variable `SOME_VAR` with the desired value.
|
|
55
|
+
The `vars.SOME_VAR` expression will then resolve correctly.
|
|
56
|
+
|
|
57
|
+
Option 2 — Provide a fallback default using the `||` operator in the caller workflow:
|
|
58
|
+
Pass `${{ vars.SOME_VAR || 'default-value' }}` to avoid passing an empty string when
|
|
59
|
+
the variable is unset. The reusable workflow input then receives `'default-value'`
|
|
60
|
+
instead of `''`.
|
|
61
|
+
|
|
62
|
+
Option 3 — Use the `inputs.` default in the reusable workflow definition:
|
|
63
|
+
In the reusable workflow's `on.workflow_call.inputs`, set a `default:` value so the
|
|
64
|
+
input has a sensible fallback even when the caller passes an empty string.
|
|
65
|
+
|
|
66
|
+
For boolean inputs specifically — always coerce `vars.*` to boolean in the caller:
|
|
67
|
+
Use `${{ vars.ENABLE_FEATURE == 'true' }}` to convert the string variable value to
|
|
68
|
+
a proper boolean rather than passing the raw string.
|
|
69
|
+
fix_code:
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: 'Caller workflow — provide default fallback for undefined vars.*'
|
|
72
|
+
code: |
|
|
73
|
+
jobs:
|
|
74
|
+
call-reusable:
|
|
75
|
+
uses: org/repo/.github/workflows/reusable.yml@main
|
|
76
|
+
with:
|
|
77
|
+
# ❌ Fails with "unexpected value ''" if SOME_ARG_A is not set:
|
|
78
|
+
# some-arg-a: ${{ vars.SOME_ARG_A }}
|
|
79
|
+
|
|
80
|
+
# ✅ Provide a fallback when the variable is unset:
|
|
81
|
+
some-arg-a: ${{ vars.SOME_ARG_A || 'default-value' }}
|
|
82
|
+
|
|
83
|
+
# ✅ For boolean inputs — convert the string to actual boolean:
|
|
84
|
+
# enable-feature: ${{ vars.ENABLE_FEATURE == 'true' }}
|
|
85
|
+
|
|
86
|
+
- language: yaml
|
|
87
|
+
label: 'Reusable workflow — define a default for the input'
|
|
88
|
+
code: |
|
|
89
|
+
# In the reusable workflow definition:
|
|
90
|
+
on:
|
|
91
|
+
workflow_call:
|
|
92
|
+
inputs:
|
|
93
|
+
some-arg-a:
|
|
94
|
+
type: string
|
|
95
|
+
required: false # Make it optional if a default makes sense
|
|
96
|
+
default: 'default-value'
|
|
97
|
+
description: 'Argument A (can be overridden by vars.SOME_ARG_A in caller)'
|
|
98
|
+
|
|
99
|
+
# ✅ Now even if caller passes '' or omits the input, the default is used
|
|
100
|
+
|
|
101
|
+
- language: yaml
|
|
102
|
+
label: 'Caller workflow — coerce vars.* string to boolean for boolean inputs'
|
|
103
|
+
code: |
|
|
104
|
+
jobs:
|
|
105
|
+
call-reusable:
|
|
106
|
+
uses: org/repo/.github/workflows/reusable.yml@main
|
|
107
|
+
with:
|
|
108
|
+
# ❌ Passing vars.ENABLE_FEATURE ('true'/'false' string or '') to bool input:
|
|
109
|
+
# enable-feature: ${{ vars.ENABLE_FEATURE }}
|
|
110
|
+
|
|
111
|
+
# ✅ Coerce the string value to an actual boolean:
|
|
112
|
+
enable-feature: ${{ vars.ENABLE_FEATURE == 'true' }}
|
|
113
|
+
prevention:
|
|
114
|
+
- "Use `vars.*` only for configuration variables set in the GitHub repository/organization/environment Settings UI — not for `env:` YAML workflow variables"
|
|
115
|
+
- "Always provide a `||` fallback when passing `vars.*` to a reusable workflow input: `${{ vars.MY_VAR || 'default' }}`"
|
|
116
|
+
- "For boolean reusable workflow inputs, coerce `vars.*` strings with `${{ vars.FLAG == 'true' }}` instead of passing the raw string"
|
|
117
|
+
- "Verify the variable name and scope in Settings → Secrets and variables → Actions → Variables before using it in a workflow"
|
|
118
|
+
- "Set `required: false` with a `default:` in the reusable workflow definition to make inputs resilient to empty string values"
|
|
119
|
+
docs:
|
|
120
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/variables#using-the-vars-context-to-access-configuration-variable-values'
|
|
121
|
+
label: 'GitHub Docs — vars context for configuration variables (distinct from env: YAML variables)'
|
|
122
|
+
- url: 'https://docs.github.com/en/actions/sharing-automations/reusing-workflows#using-inputs-and-secrets-in-a-reusable-workflow'
|
|
123
|
+
label: 'GitHub Docs — Using inputs in a reusable workflow (types, required, defaults)'
|
|
124
|
+
- url: 'https://stackoverflow.com/questions/79949037/consumers-callers-vars-value-passed-and-blank-strings-for-reusable-inputs'
|
|
125
|
+
label: "Stack Overflow — Consumer's/caller's vars value passed and blank strings for reusable inputs (May 2026)"
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
id: yaml-syntax-079
|
|
2
|
+
title: 'Invalid (non-IANA) timezone value in on.schedule.cron — actionlint "invalid timezone must be a valid IANA timezone name" + GitHub workflow validation failure'
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- schedule
|
|
7
|
+
- cron
|
|
8
|
+
- timezone
|
|
9
|
+
- IANA
|
|
10
|
+
- actionlint
|
|
11
|
+
- validation
|
|
12
|
+
- common-mistake
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'invalid timezone.*must be a valid IANA timezone name'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'invalid timezone.*schedule event'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'schedule.*timezone.*not.*valid.*IANA|IANA.*timezone.*invalid.*schedule'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
error_messages:
|
|
21
|
+
- 'invalid timezone "PST" in schedule event. it must be a valid IANA timezone name [events]'
|
|
22
|
+
- 'invalid timezone "EST" in schedule event. it must be a valid IANA timezone name [events]'
|
|
23
|
+
- 'invalid timezone "Asia/Somewhere" in schedule event. it must be a valid IANA timezone name [events]'
|
|
24
|
+
- 'invalid timezone "UTC+5" in schedule event. it must be a valid IANA timezone name [events]'
|
|
25
|
+
- 'invalid timezone "India" in schedule event. it must be a valid IANA timezone name [events]'
|
|
26
|
+
root_cause: |
|
|
27
|
+
GitHub Actions added IANA timezone support for scheduled workflows in March 2026. The
|
|
28
|
+
`timezone:` field under `on.schedule` accepts an IANA Time Zone Database name
|
|
29
|
+
(e.g., `America/New_York`, `Europe/London`, `Asia/Tokyo`). GitHub and actionlint both
|
|
30
|
+
validate that the provided string is a recognized IANA timezone name.
|
|
31
|
+
|
|
32
|
+
The most common mistakes are using timezone ABBREVIATIONS or OFFSET STRINGS instead
|
|
33
|
+
of proper IANA names:
|
|
34
|
+
|
|
35
|
+
| Invalid (rejected) | Valid IANA replacement |
|
|
36
|
+
|------------------------|---------------------------------|
|
|
37
|
+
| PST | America/Los_Angeles |
|
|
38
|
+
| EST | America/New_York |
|
|
39
|
+
| CST | America/Chicago |
|
|
40
|
+
| MST | America/Denver |
|
|
41
|
+
| BST | Europe/London |
|
|
42
|
+
| CET / CEST | Europe/Berlin or Europe/Paris |
|
|
43
|
+
| IST | Asia/Kolkata |
|
|
44
|
+
| JST | Asia/Tokyo |
|
|
45
|
+
| AEST | Australia/Sydney |
|
|
46
|
+
| UTC+5 / GMT+5 / +05:30 | Asia/Karachi or Asia/Kolkata |
|
|
47
|
+
| India | Asia/Kolkata |
|
|
48
|
+
|
|
49
|
+
IANA timezone abbreviations (PST, EST, etc.) are NOT part of the IANA Time Zone
|
|
50
|
+
Database specification — they are informal abbreviations used in human communication
|
|
51
|
+
that map ambiguously to multiple official zones. Similarly, UTC offset strings like
|
|
52
|
+
"UTC+5" or "GMT-8" are not valid IANA names.
|
|
53
|
+
|
|
54
|
+
The only valid form is the `Region/City` (or `Region/Sub-Region/City`) format used
|
|
55
|
+
in the IANA TZ database, or the special zones like `UTC`, `Etc/UTC`, `Etc/GMT+N`.
|
|
56
|
+
|
|
57
|
+
Note: actionlint validates IANA timezone strings as of v0.7.4 (March 30, 2026),
|
|
58
|
+
released to support the new `timezone:` field. The check was further fixed in commit
|
|
59
|
+
f48c0a4 to ensure completeness of the timezone validation.
|
|
60
|
+
fix: |
|
|
61
|
+
Replace the timezone abbreviation or offset string with the correct IANA timezone name.
|
|
62
|
+
The IANA Time Zone Database uses `Region/City` format (e.g., `America/New_York`).
|
|
63
|
+
|
|
64
|
+
You can look up your timezone at:
|
|
65
|
+
- https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
|
66
|
+
- https://www.iana.org/time-zones
|
|
67
|
+
- https://nodatime.org/TimeZones (interactive timezone selector)
|
|
68
|
+
|
|
69
|
+
Common fixes:
|
|
70
|
+
- `timezone: PST` → `timezone: America/Los_Angeles`
|
|
71
|
+
- `timezone: EST` → `timezone: America/New_York`
|
|
72
|
+
- `timezone: IST` → `timezone: Asia/Kolkata`
|
|
73
|
+
- `timezone: UTC+5:30` → `timezone: Asia/Kolkata`
|
|
74
|
+
fix_code:
|
|
75
|
+
- language: yaml
|
|
76
|
+
label: 'Use IANA timezone names — common US timezone fixes'
|
|
77
|
+
code: |
|
|
78
|
+
on:
|
|
79
|
+
schedule:
|
|
80
|
+
# ❌ PST is NOT a valid IANA name:
|
|
81
|
+
# - cron: '0 9 * * 1-5'
|
|
82
|
+
# timezone: 'PST'
|
|
83
|
+
|
|
84
|
+
# ✅ Use the IANA Region/City format:
|
|
85
|
+
- cron: '0 9 * * 1-5'
|
|
86
|
+
timezone: 'America/Los_Angeles' # Pacific (handles PST/PDT automatically)
|
|
87
|
+
|
|
88
|
+
# Other US regions:
|
|
89
|
+
# timezone: 'America/New_York' # Eastern (EST/EDT)
|
|
90
|
+
# timezone: 'America/Chicago' # Central (CST/CDT)
|
|
91
|
+
# timezone: 'America/Denver' # Mountain (MST/MDT)
|
|
92
|
+
# timezone: 'Pacific/Honolulu' # Hawaii (HST)
|
|
93
|
+
|
|
94
|
+
- language: yaml
|
|
95
|
+
label: 'Use IANA timezone names — India, Europe, Asia fixes'
|
|
96
|
+
code: |
|
|
97
|
+
on:
|
|
98
|
+
schedule:
|
|
99
|
+
# ❌ IST is NOT a valid IANA name:
|
|
100
|
+
# - cron: '30 9 * * *'
|
|
101
|
+
# timezone: 'IST'
|
|
102
|
+
|
|
103
|
+
# ✅ India Standard Time:
|
|
104
|
+
- cron: '30 9 * * *'
|
|
105
|
+
timezone: 'Asia/Kolkata'
|
|
106
|
+
|
|
107
|
+
# Other common IANA names:
|
|
108
|
+
# timezone: 'Europe/London' # UK (BST/GMT)
|
|
109
|
+
# timezone: 'Europe/Berlin' # Germany (CET/CEST)
|
|
110
|
+
# timezone: 'Europe/Paris' # France (CET/CEST)
|
|
111
|
+
# timezone: 'Asia/Tokyo' # Japan (JST)
|
|
112
|
+
# timezone: 'Asia/Shanghai' # China (CST)
|
|
113
|
+
# timezone: 'Australia/Sydney' # Australia Eastern (AEST/AEDT)
|
|
114
|
+
|
|
115
|
+
- language: yaml
|
|
116
|
+
label: 'Fix UTC offset strings — use Etc/GMT or a Region/City name'
|
|
117
|
+
code: |
|
|
118
|
+
on:
|
|
119
|
+
schedule:
|
|
120
|
+
# ❌ UTC offset strings are NOT valid IANA names:
|
|
121
|
+
# - cron: '0 8 * * *'
|
|
122
|
+
# timezone: 'UTC+5:30' # wrong
|
|
123
|
+
# - cron: '0 8 * * *'
|
|
124
|
+
# timezone: 'GMT+5' # wrong
|
|
125
|
+
|
|
126
|
+
# ✅ Use the Region/City form instead:
|
|
127
|
+
- cron: '0 8 * * *'
|
|
128
|
+
timezone: 'Asia/Karachi' # UTC+5 (Pakistan)
|
|
129
|
+
|
|
130
|
+
# Note: Etc/GMT zones use inverted sign convention (POSIX):
|
|
131
|
+
# Etc/GMT-5 is UTC+5 (confusingly inverted) — prefer Region/City names
|
|
132
|
+
prevention:
|
|
133
|
+
- "Always use `Region/City` IANA timezone names (e.g., `America/New_York`) — never abbreviations like PST, EST, IST"
|
|
134
|
+
- "Look up the correct IANA name using the Wikipedia tz database list before using a new timezone"
|
|
135
|
+
- "Run `actionlint` locally (v0.7.4+) before pushing — it validates IANA timezone strings in on.schedule"
|
|
136
|
+
- "Use `UTC` explicitly for UTC schedules — it is a valid IANA name unlike `GMT+0` or `UTC+0`"
|
|
137
|
+
- "Remember that IANA names handle DST automatically (America/New_York switches between EST and EDT); abbreviations like EST are fixed-offset and ambiguous"
|
|
138
|
+
docs:
|
|
139
|
+
- url: 'https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#onschedule'
|
|
140
|
+
label: 'GitHub Docs — on.schedule syntax including timezone field'
|
|
141
|
+
- url: 'https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'
|
|
142
|
+
label: 'Wikipedia — List of IANA tz database timezone names'
|
|
143
|
+
- url: 'https://github.com/rhysd/actionlint/blob/main/docs/checks.md#cron-syntax-and-iana-timezone-string-at-onschedule'
|
|
144
|
+
label: 'actionlint docs — CRON syntax and IANA timezone string validation at on.schedule'
|
|
145
|
+
- url: 'https://github.com/rhysd/actionlint/issues/638'
|
|
146
|
+
label: 'actionlint #638 — Add support for timezone schedule triggers (fixed in v0.7.4)'
|
|
147
|
+
- url: 'https://github.com/rhysd/actionlint/commit/f48c0a493f9e25e99443136b413cde503258c745'
|
|
148
|
+
label: 'actionlint commit f48c0a4 — fix timezone check is incomplete (March 30, 2026)'
|
package/package.json
CHANGED