@htekdev/actions-debugger 1.0.118 → 1.0.120

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.
@@ -0,0 +1,124 @@
1
+ id: known-unsolved-068
2
+ title: "Step outcome cannot distinguish timeout from failure — both report as 'failure' in steps context"
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - timeout-minutes
7
+ - outcome
8
+ - conclusion
9
+ - continue-on-error
10
+ - steps-context
11
+ - retry
12
+ - known-limitation
13
+ - no-fix
14
+ patterns:
15
+ - regex: 'steps\.\w+\.outcome\s*==\s*.failure.'
16
+ flags: 'i'
17
+ - regex: 'timeout-minutes.*continue-on-error|continue-on-error.*timeout-minutes'
18
+ flags: 'im'
19
+ - regex: 'The process.*timed out after \d+ minutes'
20
+ flags: 'i'
21
+ error_messages:
22
+ - "Error: The process '/usr/bin/bash' failed with exit code 1"
23
+ - 'Error: Process completed with exit code 1'
24
+ root_cause: |
25
+ GitHub Actions exposes two result fields for completed steps in the steps context:
26
+
27
+ - steps.<id>.outcome: the raw result before continue-on-error is applied.
28
+ Possible values: success, failure, cancelled, skipped.
29
+ - steps.<id>.conclusion: the final result after continue-on-error is applied.
30
+ When continue-on-error: true is set on a failed step, conclusion becomes 'success'
31
+ even if outcome is 'failure'.
32
+
33
+ Neither field distinguishes between a step that failed because the process exited with a
34
+ non-zero code and a step that failed because it hit its timeout-minutes limit. Both
35
+ scenarios set outcome to 'failure'. There is no 'timed_out' value, no
36
+ steps.<id>.timed_out boolean, and no built-in expression function to query the reason
37
+ for failure.
38
+
39
+ This means workflows cannot natively:
40
+ - Retry only on timeout while failing fast on real errors
41
+ - Alert with different severity for timeouts vs application failures
42
+ - Auto-escalate timeout-minutes only when a timeout (not a logic error) occurred
43
+
44
+ The limitation has been a known open request in the GitHub Actions community since at
45
+ least 2022 with no current implementation timeline from GitHub.
46
+ fix: |
47
+ No native fix exists within GitHub Actions expressions. Two manual workarounds are
48
+ available in bash-based steps:
49
+
50
+ 1. Record start time and compute elapsed duration at the next step to infer timeout:
51
+ Compare elapsed seconds against the timeout-minutes threshold. A step that used
52
+ approximately 100% of its time budget likely timed out.
53
+
54
+ 2. Write a sentinel file just before the critical work; check for its absence afterward.
55
+ A timed-out step never reaches the sentinel-write line after the long-running command,
56
+ while a normally-failing step (which exits immediately on error) may or may not.
57
+
58
+ Neither workaround is exact — both have race conditions and edge cases. The most
59
+ reliable approach is to implement timeout detection inside the script itself using
60
+ shell signals or test-framework timeout flags.
61
+ fix_code:
62
+ - language: yaml
63
+ label: 'Workaround 1: Infer timeout via elapsed time'
64
+ code: |
65
+ - name: Start timer
66
+ id: timer
67
+ run: echo "start=$(date +%s)" >> "$GITHUB_OUTPUT"
68
+
69
+ - name: Run slow tests
70
+ id: tests
71
+ timeout-minutes: 10
72
+ continue-on-error: true
73
+ run: npm test
74
+
75
+ - name: Classify failure type
76
+ if: steps.tests.outcome == 'failure'
77
+ env:
78
+ START: ${{ steps.timer.outputs.start }}
79
+ run: |
80
+ elapsed=$(( $(date +%s) - START ))
81
+ timeout_secs=600 # 10 minutes in seconds
82
+ threshold=$(( timeout_secs - 30 )) # within 30s of limit → likely timeout
83
+ if [ "$elapsed" -ge "$threshold" ]; then
84
+ echo "::warning::Step likely timed out (elapsed ${elapsed}s, limit ${timeout_secs}s)"
85
+ # Handle timeout-specific logic here (e.g., don't fail, just warn)
86
+ else
87
+ echo "::error::Step failed (exit code, not timeout — elapsed ${elapsed}s)"
88
+ exit 1
89
+ fi
90
+ - language: yaml
91
+ label: 'Workaround 2: Sentinel file to detect timeout vs normal failure'
92
+ code: |
93
+ - name: Run tests with sentinel
94
+ id: tests
95
+ timeout-minutes: 10
96
+ continue-on-error: true
97
+ run: |
98
+ # The long-running command:
99
+ npm test
100
+ # Only reached on clean exit (not timeout, not error):
101
+ touch /tmp/test-completed
102
+
103
+ - name: Check failure reason
104
+ if: steps.tests.outcome == 'failure'
105
+ run: |
106
+ if [ ! -f /tmp/test-completed ]; then
107
+ echo "Step timed out or failed before completing"
108
+ # Inspect logs for timeout keyword:
109
+ # If the runner log shows "The process timed out after N minutes" → it was timeout
110
+ else
111
+ echo "Step completed but exited non-zero — application failure"
112
+ exit 1
113
+ fi
114
+ prevention:
115
+ - 'Log test durations inside the script itself; test framework flags like --testTimeout (Jest) or --timeout (Mocha) provide per-test granularity inside logs.'
116
+ - 'Use separate jobs for steps with different timeout characteristics — a dedicated integration-test job with a high timeout-minutes and a unit-test job with a low one makes failures easier to categorize.'
117
+ - 'If the step runs a single long command, wrap it in a shell timeout with a slightly shorter duration than timeout-minutes; the shell timeout exit code (124) is detectable inside the same step.'
118
+ docs:
119
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#steps-context'
120
+ label: 'GitHub Docs: steps context — outcome and conclusion fields'
121
+ - url: 'https://stackoverflow.com/questions/78233438/github-action-cannot-get-timeout-status-from-previous-step'
122
+ label: 'SO: Cannot get timeout status from previous step (Mar 2024)'
123
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-conditions-to-control-job-execution'
124
+ label: 'GitHub Docs: Status check functions (failure, success, cancelled, always)'
@@ -0,0 +1,127 @@
1
+ id: known-unsolved-069
2
+ title: "Matrix Strategy Hard Limit: 256 Jobs Per Workflow Run"
3
+ category: known-unsolved
4
+ severity: error
5
+ tags:
6
+ - matrix
7
+ - strategy
8
+ - job-limit
9
+ - dynamic-matrix
10
+ - fromjson
11
+ - scalability
12
+ patterns:
13
+ - regex: 'matrix.*generates.*too many|too many.*entries.*matrix'
14
+ flags: 'i'
15
+ - regex: 'limited to 256|exceeding.*allowed maximum.*256|256.*jobs.*per.*workflow'
16
+ flags: 'i'
17
+ error_messages:
18
+ - "Matrix generates too many entries. Limited to 256."
19
+ root_cause: |
20
+ GitHub Actions enforces a hard limit of 256 jobs per workflow run across
21
+ all matrix dimensions. This is calculated as the total Cartesian product of
22
+ all matrix axes after applying include/exclude entries.
23
+
24
+ Common triggers:
25
+ - Dynamic matrices from fromJSON output that grow over time as new targets
26
+ are added to a test matrix config file
27
+ - Multi-dimensional matrices (os x language x version) where the product
28
+ of all axis sizes exceeds 256
29
+ - Adding include: entries on top of an already-large base matrix, each
30
+ include: adds one additional job that counts against the 256 limit
31
+ - Monorepo CI matrices that test all services x all supported runtimes
32
+
33
+ The error fires immediately when the workflow is queued and prevents any
34
+ jobs from running. GitHub reports the total generated count in the error
35
+ message: "Matrix generates N entries. Limited to 256."
36
+
37
+ This limit applies equally to GitHub-hosted and self-hosted runners and
38
+ cannot be increased by contacting support.
39
+ fix: |
40
+ The 256-job-per-run limit is hard and cannot be increased. Options:
41
+
42
+ 1. Split across multiple workflow files, each handling a subset of targets.
43
+ Trigger them from a parent coordinator workflow or via separate triggers.
44
+
45
+ 2. Reduce matrix dimensions: test only meaningful combinations using
46
+ explicit include: lists rather than full Cartesian products. Not every
47
+ OS x language x version triplet needs to be tested.
48
+
49
+ 3. Chunk using workflow_dispatch fan-out: a generator job creates N chunks
50
+ of at most 256 items and dispatches sub-workflow runs per chunk via
51
+ repository_dispatch or workflow_dispatch API calls.
52
+
53
+ 4. Use a single job with a shell loop for some dimensions: instead of
54
+ separate matrix jobs per version, loop over versions inside one job step.
55
+ Sacrifices parallelism but avoids the limit entirely.
56
+ fix_code:
57
+ - language: yaml
58
+ label: "Broken — Cartesian product silently grows past 256 as versions are added"
59
+ code: |
60
+ strategy:
61
+ matrix:
62
+ os: [ubuntu-22.04, ubuntu-24.04, windows-2022, macos-14, macos-15]
63
+ node: [18, 20, 22, 23]
64
+ python: ['3.9', '3.10', '3.11', '3.12', '3.13']
65
+ # 5 x 4 x 5 = 100 jobs today — fine, but adding node 24 pushes to 125,
66
+ # adding python 3.14 pushes to 150, and so on until 256 is silently hit
67
+
68
+ - language: yaml
69
+ label: "Fixed — explicit include list tests only meaningful combinations"
70
+ code: |
71
+ strategy:
72
+ matrix:
73
+ include:
74
+ # Test LTS node on primary OS with latest stable Python
75
+ - os: ubuntu-24.04
76
+ node: 20
77
+ python: '3.12'
78
+ - os: ubuntu-24.04
79
+ node: 22
80
+ python: '3.12'
81
+ # Windows only gets the most recent LTS
82
+ - os: windows-2022
83
+ node: 20
84
+ python: '3.11'
85
+ # macOS gets one combination
86
+ - os: macos-15
87
+ node: 20
88
+ python: '3.12'
89
+ # Total: 4 jobs instead of 100+ from full Cartesian product
90
+
91
+ - language: yaml
92
+ label: "Defensive — validate dynamic matrix size before passing to strategy"
93
+ code: |
94
+ jobs:
95
+ generate-matrix:
96
+ runs-on: ubuntu-latest
97
+ outputs:
98
+ matrix: ${{ steps.gen.outputs.matrix }}
99
+ steps:
100
+ - id: gen
101
+ run: |
102
+ MATRIX=$(cat test-matrix.json)
103
+ COUNT=$(echo "$MATRIX" | jq '.include | length')
104
+ if [ "$COUNT" -gt 256 ]; then
105
+ echo "::error::Matrix has $COUNT entries (limit: 256). Split into multiple workflows."
106
+ exit 1
107
+ fi
108
+ echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"
109
+
110
+ test:
111
+ needs: generate-matrix
112
+ strategy:
113
+ matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }}
114
+ runs-on: ${{ matrix.os }}
115
+ steps:
116
+ - run: echo "Testing ${{ matrix.os }} / ${{ matrix.version }}"
117
+ prevention:
118
+ - "Before adding a new matrix axis, calculate the new total job count — it must stay at or below 256."
119
+ - "For dynamic matrices (fromJSON), validate the output list length in the generator job and fail fast if > 256."
120
+ - "Prefer explicit include: lists over full Cartesian products when not all dimension combinations are meaningful."
121
+ - "Monitor matrix job counts over time: a previously-safe matrix silently crosses 256 as new versions are added."
122
+ - "Document the current job count in a comment next to the matrix block so reviewers notice when PRs push it higher."
123
+ docs:
124
+ - url: "https://docs.github.com/en/actions/reference/limits"
125
+ label: "GitHub Actions Limits — Job Matrix: 256 jobs per workflow run"
126
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow"
127
+ label: "GitHub Docs — Using a matrix for your jobs"
@@ -0,0 +1,136 @@
1
+ id: permissions-auth-070
2
+ title: "GITHUB_TOKEN packages:write Cannot Delete GitHub Container Registry Packages"
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - github-packages
7
+ - ghcr
8
+ - container-registry
9
+ - packages-delete
10
+ - permissions
11
+ - pat
12
+ patterns:
13
+ - regex: 'delete.*package.*403|package.*delete.*forbidden'
14
+ flags: 'i'
15
+ - regex: 'Resource not accessible by integration'
16
+ flags: 'i'
17
+ error_messages:
18
+ - "Resource not accessible by integration"
19
+ - "HttpError: Resource not accessible by integration"
20
+ - "403 Forbidden"
21
+ root_cause: |
22
+ GitHub Container Registry (ghcr.io) and other granular-permission package
23
+ registries (npm, NuGet, RubyGems on ghpr) use package-level access control
24
+ that is separate from repository permissions. The GITHUB_TOKEN workflow
25
+ permission block supports packages: write, which grants the ability to:
26
+ - Publish (push) new package versions
27
+ - Update package metadata and visibility
28
+
29
+ However, packages: write does NOT grant the ability to DELETE package
30
+ versions. Deletion of granular-permission packages requires the delete:packages
31
+ scope, which exists only on classic personal access tokens (PATs) — there is
32
+ no equivalent permission in the GITHUB_TOKEN fine-grained model.
33
+
34
+ As a result, workflows that use actions/delete-package-versions (or make
35
+ direct REST API calls to DELETE /user/packages/{type}/{name}/versions/{id}
36
+ or DELETE /orgs/{org}/packages/{type}/{package_name}/versions/{id}) always
37
+ fail with 403 "Resource not accessible by integration" even with:
38
+ permissions:
39
+ packages: write
40
+
41
+ Note: Repository-scoped packages (Apache Maven, legacy Gradle) that inherit
42
+ repository permissions may behave differently — deletion can work via the
43
+ repo admin role. Container Registry packages always use granular permissions.
44
+ fix: |
45
+ Replace GITHUB_TOKEN with a token that carries delete:packages authority.
46
+
47
+ Option 1 (recommended for orgs): GitHub App token
48
+ - Create a GitHub App with "Packages: Read & Write" and "Packages: Admin"
49
+ permissions
50
+ - Install the app on the org or repo
51
+ - Use actions/create-github-app-token to generate a token in the workflow
52
+ - Pass the token to the deletion step
53
+
54
+ Option 2 (simpler for personal/small-team repos): Classic PAT
55
+ - Generate a classic PAT (Settings > Developer settings > Personal access
56
+ tokens > Tokens (classic)) with the delete:packages scope
57
+ - Store as a repository or organization secret
58
+ - Reference the secret in the deletion step
59
+
60
+ Option 3 (repo-scoped packages only — Apache Maven, Gradle):
61
+ - Ensure the package uses repository-scoped permissions (not granular)
62
+ - Grant the workflow admin access to the repository
63
+ - This does NOT apply to ghcr.io container images, which always require
64
+ a PAT or App approach for deletion
65
+ fix_code:
66
+ - language: yaml
67
+ label: "Broken — GITHUB_TOKEN packages:write cannot delete container images"
68
+ code: |
69
+ permissions:
70
+ packages: write # Grants publish access but NOT delete access
71
+
72
+ jobs:
73
+ cleanup:
74
+ runs-on: ubuntu-latest
75
+ steps:
76
+ - name: Delete old versions
77
+ uses: actions/delete-package-versions@v5
78
+ with:
79
+ package-name: my-image
80
+ package-type: container
81
+ min-versions-to-keep: 5
82
+ token: ${{ secrets.GITHUB_TOKEN }} # Fails with 403 for container packages
83
+
84
+ - language: yaml
85
+ label: "Fixed — classic PAT with delete:packages scope"
86
+ code: |
87
+ permissions:
88
+ packages: write
89
+
90
+ jobs:
91
+ cleanup:
92
+ runs-on: ubuntu-latest
93
+ steps:
94
+ - name: Delete old versions
95
+ uses: actions/delete-package-versions@v5
96
+ with:
97
+ package-name: my-image
98
+ package-type: container
99
+ min-versions-to-keep: 5
100
+ # Classic PAT must have: write:packages + delete:packages scopes
101
+ token: ${{ secrets.PAT_DELETE_PACKAGES }}
102
+
103
+ - language: yaml
104
+ label: "Fixed — GitHub App token (recommended for organizations)"
105
+ code: |
106
+ jobs:
107
+ cleanup:
108
+ runs-on: ubuntu-latest
109
+ steps:
110
+ - name: Generate GitHub App token
111
+ id: app-token
112
+ uses: actions/create-github-app-token@v1
113
+ with:
114
+ app-id: ${{ vars.PACKAGE_CLEANUP_APP_ID }}
115
+ private-key: ${{ secrets.PACKAGE_CLEANUP_APP_KEY }}
116
+
117
+ - name: Delete old container image versions
118
+ uses: actions/delete-package-versions@v5
119
+ with:
120
+ package-name: my-image
121
+ package-type: container
122
+ min-versions-to-keep: 5
123
+ token: ${{ steps.app-token.outputs.token }}
124
+ prevention:
125
+ - "Never assume packages:write grants deletion rights — it only covers publish and metadata operations."
126
+ - "Provision a dedicated classic PAT or GitHub App with package admin access for all cleanup/retention workflows."
127
+ - "Document which secrets are needed for deletion workflows so maintainers do not accidentally replace them with GITHUB_TOKEN."
128
+ - "Audit all uses of actions/delete-package-versions in your org to confirm each workflow uses an appropriate token."
129
+ - "Prefer GitHub App tokens over classic PATs for org workflows — PAT credentials are tied to a specific user account."
130
+ docs:
131
+ - url: "https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages"
132
+ label: "GitHub Docs — About permissions for GitHub Packages (granular vs repository-scoped)"
133
+ - url: "https://github.com/actions/delete-package-versions"
134
+ label: "actions/delete-package-versions — token requirements"
135
+ - url: "https://docs.github.com/en/rest/packages/packages"
136
+ label: "GitHub REST API — Packages DELETE endpoints"
@@ -0,0 +1,107 @@
1
+ id: runner-environment-214
2
+ title: 'setup-java Fails with "Invalid Version" for JetBrains Runtime 4-Part Semver (e.g. 17.0.8.1+1080.1)'
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - setup-java
7
+ - jbr
8
+ - jetbrains-runtime
9
+ - java
10
+ - semver
11
+ - version-parsing
12
+ - windows
13
+ - ubuntu
14
+ patterns:
15
+ - regex: 'Java setup process failed due to:\s*Invalid Version:\s*\d+\.\d+\.\d+\.\d+\+'
16
+ flags: 'i'
17
+ - regex: 'Invalid Version:\s*\d+\.\d+\.\d+\.\d+'
18
+ flags: 'i'
19
+ - regex: 'java setup.*failed.*Invalid Version'
20
+ flags: 'i'
21
+ error_messages:
22
+ - 'Error: Java setup process failed due to: Invalid Version: 17.0.8.1+1080.1'
23
+ - 'Error: Java setup process failed due to: Invalid Version: 25.0.3+480.61'
24
+ - 'Error: Java setup process failed due to: Invalid Version: 21.0.5.1+1100.2'
25
+ root_cause: |
26
+ JetBrains Runtime (JBR) version strings use a 4-part numeric format:
27
+ `major.minor.patch.update+build.sub-build` (e.g. `17.0.8.1+1080.1`).
28
+ This does NOT conform to standard semantic versioning (3-part numeric).
29
+
30
+ The `actions/setup-java` version resolver uses a semver parser internally.
31
+ When the user passes a full pinned JBR version string containing a 4-part
32
+ numeric prefix (the `.1` after the patch component), the parser throws:
33
+
34
+ "Invalid Version: 17.0.8.1+1080.1"
35
+
36
+ Affected distributions: `jetbrains` (JBR) distribution only.
37
+ Affected platforms: Ubuntu and Windows (both throw the same error).
38
+ Unaffected: Oracle, Temurin, Adopt, Corretto and other distributions whose
39
+ version strings use standard 3-part semver (e.g. `21.0.3+9`).
40
+
41
+ The root cause is that JBR independently versions patch updates
42
+ (the `.1` suffix), meaning a JBR patch release version is 4 integers deep,
43
+ which the bundled semver parser does not accept.
44
+
45
+ Tracked upstream: https://github.com/actions/setup-java/issues/1008 (open May 2026)
46
+ Fix PR: https://github.com/actions/setup-java/pull/1009 (isVersionSatisfies patch — pending merge)
47
+ fix: |
48
+ Until https://github.com/actions/setup-java/pull/1009 is merged and released,
49
+ do NOT pin the full 4-part JBR version string. Use either:
50
+
51
+ 1. The 3-part version prefix (major.minor.patch) — setup-java will resolve the
52
+ latest available JBR sub-build for that patch:
53
+
54
+ - uses: actions/setup-java@v5
55
+ with:
56
+ distribution: 'jetbrains'
57
+ java-version: '17.0.8' # omit the .1 suffix
58
+
59
+ 2. Just the major version for the latest JBR:
60
+
61
+ - uses: actions/setup-java@v5
62
+ with:
63
+ distribution: 'jetbrains'
64
+ java-version: '17'
65
+
66
+ 3. The pre-release format accepted by the current parser (major.minor.patch
67
+ with the build metadata only, omitting the update digit):
68
+
69
+ - uses: actions/setup-java@v5
70
+ with:
71
+ distribution: 'jetbrains'
72
+ java-version: '17.0.8+1080.1' # skip the 4th integer; use build metadata
73
+ fix_code:
74
+ - language: yaml
75
+ label: 'Use 3-part version prefix to avoid "Invalid Version" on JBR'
76
+ code: |
77
+ - name: Set up JBR 17 (JetBrains Runtime)
78
+ uses: actions/setup-java@v5
79
+ with:
80
+ distribution: 'jetbrains'
81
+ java-version: '17.0.8' # NOT '17.0.8.1+1080.1' — 4-part fails the parser
82
+ - language: yaml
83
+ label: 'Major-version pin (simplest, always resolves latest JBR for that major)'
84
+ code: |
85
+ - name: Set up JBR 25
86
+ uses: actions/setup-java@v5
87
+ with:
88
+ distribution: 'jetbrains'
89
+ java-version: '25'
90
+ prevention:
91
+ - 'Check the JBR releases page to understand the version format before pinning.
92
+ JBR versions are `major.minor.patch.update+build.sub-build`; only use the
93
+ 3-part prefix (major.minor.patch) or major-version-only with setup-java.'
94
+ - 'Watch https://github.com/actions/setup-java/pull/1009 for when the fix merges
95
+ so full 4-part JBR version strings become accepted.'
96
+ - 'For reproducibility without pinning the exact JBR sub-build, consider
97
+ using a `.java-version` file with a 3-part version string and referencing it
98
+ via `java-version-file:` in setup-java.'
99
+ docs:
100
+ - url: 'https://github.com/actions/setup-java/issues/1008'
101
+ label: 'setup-java #1008 — Error parsing JBR version 17.0.8.1+1080.1 (open, May 2026)'
102
+ - url: 'https://github.com/actions/setup-java/pull/1009'
103
+ label: 'setup-java PR #1009 — fix: reject non-semver candidate versions in isVersionSatisfies (pending)'
104
+ - url: 'https://github.com/actions/setup-java?tab=readme-ov-file#supported-distributions'
105
+ label: 'setup-java README — Supported distributions (JetBrains / JBR)'
106
+ - url: 'https://github.com/JetBrains/JetBrainsRuntime/releases'
107
+ label: 'JetBrains Runtime releases — version format reference'
@@ -0,0 +1,93 @@
1
+ id: runner-environment-215
2
+ title: 'setup-node fails with EBADDEVENGINES when devEngines.packageManager.version does not match initial npm'
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - setup-node
7
+ - npm
8
+ - devEngines
9
+ - EBADDEVENGINES
10
+ - package-manager
11
+ - npm-version
12
+ patterns:
13
+ - regex: 'npm error code EBADDEVENGINES'
14
+ flags: 'i'
15
+ - regex: 'npm error EBADDEVENGINES.*Invalid devEngines\.packageManager'
16
+ flags: 'i'
17
+ - regex: 'npm error EBADDEVENGINES.*Invalid semver version.*does not match.*for "packageManager"'
18
+ flags: 'i'
19
+ - regex: '/npm config get cache\s*npm error code EBADDEVENGINES'
20
+ flags: 'is'
21
+ error_messages:
22
+ - 'npm error code EBADDEVENGINES'
23
+ - 'npm error EBADDEVENGINES The developer of this package has specified the following through devEngines'
24
+ - 'npm error EBADDEVENGINES Invalid devEngines.packageManager'
25
+ - 'npm error EBADDEVENGINES Invalid semver version "^11.10.0" does not match "10.9.7" for "packageManager"'
26
+ root_cause: |
27
+ actions/setup-node@v6 runs `npm config get cache` immediately after installing Node.js to
28
+ determine the npm cache path before activating the package-manager-cache feature. If
29
+ package.json declares a `devEngines.packageManager.version` constraint (Node.js 22.6+
30
+ feature) that the bundled npm does NOT satisfy, npm exits with EBADDEVENGINES before
31
+ returning the cache path.
32
+
33
+ The failure happens before setup-node has a chance to upgrade npm to the required version,
34
+ creating a catch-22: the action needs the cache path to set up, but npm won't answer
35
+ because the version constraint is not yet met.
36
+
37
+ Common scenario: project requires `npm@^11.10.0` (for `min-release-age` or other features
38
+ added in npm 11.x), but Node 22 ships with npm 10.x. The first `npm` invocation fails
39
+ immediately when devEngines enforcement is active.
40
+ fix: |
41
+ Option 1 (recommended) — add `"onFail": "warn"` to the devEngines.packageManager block.
42
+ This downgrades version mismatches to warnings so npm commands still complete, letting
43
+ setup-node finish. Install the required npm version in a subsequent step.
44
+
45
+ Option 2 — pin Node.js to a version whose bundled npm satisfies devEngines (often
46
+ impractical).
47
+
48
+ Option 3 — set `package-manager-cache: false` in setup-node to skip the cache-path probe
49
+ entirely and manage npm caching separately.
50
+ fix_code:
51
+ - language: yaml
52
+ label: 'Option 1 — add onFail: warn to package.json (fix the root cause)'
53
+ code: |
54
+ # In package.json:
55
+ # {
56
+ # "devEngines": {
57
+ # "packageManager": {
58
+ # "name": "npm",
59
+ # "version": "^11.10.0",
60
+ # "onFail": "warn" <-- add this
61
+ # }
62
+ # }
63
+ # }
64
+
65
+ # In workflow — install the required npm version after setup-node succeeds:
66
+ - uses: actions/setup-node@v6
67
+ with:
68
+ node-version-file: package.json
69
+ - run: npm install -g npm@^11.10.0
70
+ - language: yaml
71
+ label: 'Option 3 — disable package-manager-cache in setup-node'
72
+ code: |
73
+ - uses: actions/setup-node@v6
74
+ with:
75
+ node-version: '22'
76
+ package-manager-cache: false # skip npm config get cache probe
77
+ - run: npm install -g npm@^11.10.0
78
+ - uses: actions/cache@v4
79
+ with:
80
+ path: ~/.npm
81
+ key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
82
+ prevention:
83
+ - 'Always pair devEngines.packageManager.version with "onFail": "warn" unless the project must hard-fail on CI for version mismatches.'
84
+ - 'Use package-manager-cache: false in setup-node and handle npm caching manually when devEngines constraints are strict.'
85
+ - 'Track the Node.js release schedule to choose a version that ships with an npm satisfying devEngines requirements from day one.'
86
+ - 'Pin action version in workflow (e.g., actions/setup-node@v6.4.0) and test upgrades in a branch before rolling out.'
87
+ docs:
88
+ - url: 'https://github.com/actions/setup-node/issues/1553'
89
+ label: 'setup-node#1553 — EBADDEVENGINES blocks cache-path probe (May 2026)'
90
+ - url: 'https://docs.npmjs.com/cli/v11/configuring-npm/package-json#devengines'
91
+ label: 'npm docs — devEngines field and onFail option'
92
+ - url: 'https://github.com/nodejs/node/issues/62425'
93
+ label: 'Node.js#62425 — npm installation behavior on Node v22.22.2 with devEngines'
@@ -0,0 +1,82 @@
1
+ id: runner-environment-216
2
+ title: 'ubuntu-26.04 ships Helm 4 — `helm repo add --no-update` removed, causes unknown flag error'
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - helm
7
+ - helm-4
8
+ - ubuntu-26
9
+ - tool-upgrade
10
+ - flag-removed
11
+ patterns:
12
+ - regex: 'Error: unknown flag: --no-update'
13
+ flags: 'i'
14
+ - regex: 'unknown flag.*--no-update'
15
+ flags: 'i'
16
+ error_messages:
17
+ - 'Error: unknown flag: --no-update'
18
+ - 'Error: flag provided but not defined: --no-update'
19
+ root_cause: |
20
+ ubuntu-26.04 runner images install Helm 4 (via the `get-helm-4` installer script)
21
+ instead of Helm 3 used on ubuntu-22.04 and ubuntu-24.04. In Helm 4, the
22
+ `--no-update` flag was permanently removed from `helm repo add`
23
+ (breaking change: helm/helm#13614, released in Helm v4.0.0, November 2025).
24
+
25
+ In Helm 3, `helm repo add <name> <url> --no-update` was used to add a repo
26
+ without triggering a refresh of all configured repos. This was common in CI
27
+ pipelines to speed up `helm repo add` when many repos are configured. The
28
+ flag was first deprecated in Helm 3.7 and has been removed entirely in Helm 4
29
+ with no deprecated alias retained.
30
+
31
+ The runner-images commit 9e3319d (May 2026) introduced Helm 4 for ubuntu-26.04.
32
+ Any workflow using `runs-on: ubuntu-26.04` (or eventually `ubuntu-latest` when
33
+ it migrates to 26.04) that calls `helm repo add --no-update` will fail
34
+ immediately with `Error: unknown flag: --no-update`.
35
+ fix: |
36
+ Remove the `--no-update` flag entirely. In Helm 4, `helm repo add` does not
37
+ update existing repos by default — the behavior the flag used to enforce is
38
+ now the default behavior.
39
+
40
+ If you need to force-update all repos after adding, use `helm repo update`.
41
+
42
+ To avoid Helm version surprises on ubuntu-26.04, pin a specific Helm version
43
+ using `azure/setup-helm` and pass the version explicitly.
44
+ fix_code:
45
+ - language: yaml
46
+ label: 'Remove --no-update flag (Helm 4 default behavior)'
47
+ code: |
48
+ - name: Add Helm repo
49
+ run: |
50
+ # Helm 4 on ubuntu-26.04: --no-update flag has been removed.
51
+ # Helm 4 does not update existing repos by default (old --no-update behavior).
52
+ # Simply omit the flag:
53
+ helm repo add bitnami https://charts.bitnami.com/bitnami
54
+ # If you still need to update all repos afterwards:
55
+ # helm repo update
56
+
57
+ - language: yaml
58
+ label: 'Pin Helm version with azure/setup-helm to avoid version surprises'
59
+ code: |
60
+ - name: Set up Helm
61
+ uses: azure/setup-helm@v5
62
+ with:
63
+ version: '3.17.x' # Pin to Helm 3 if your workflow isn't Helm 4 ready
64
+ # Or pin to a specific Helm 4 version:
65
+ # version: '4.2.x'
66
+
67
+ - name: Add Helm repo
68
+ run: helm repo add bitnami https://charts.bitnami.com/bitnami
69
+ prevention:
70
+ - "Pin your Helm version with `azure/setup-helm` rather than relying on the pre-installed Helm on the runner."
71
+ - "When migrating to ubuntu-26.04, audit all `helm repo add` calls and remove any `--no-update` flags."
72
+ - "Check the Helm 4 migration guide at https://helm.sh/docs/topics/v4_migration/ before adopting ubuntu-26.04."
73
+ - "Add a CI step that prints `helm version` so version surprises are immediately visible in logs."
74
+ docs:
75
+ - url: 'https://github.com/helm/helm/blob/main/CHANGELOG.md'
76
+ label: 'Helm 4 Changelog — breaking changes'
77
+ - url: 'https://helm.sh/docs/topics/v4_migration/'
78
+ label: 'Helm v4 Migration Guide'
79
+ - url: 'https://github.com/actions/runner-images/commit/9e3319d6b4acc306925295853d0ff41ddd5c40f0'
80
+ label: 'runner-images commit: ubuntu-26 ships Helm 4 (get-helm-4)'
81
+ - url: 'https://github.com/Azure/setup-helm'
82
+ label: 'azure/setup-helm action — pin Helm version'