@htekdev/actions-debugger 1.0.132 → 1.0.133

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,100 @@
1
+ id: caching-artifacts-078
2
+ title: '`upload-artifact/merge` aborts with "Failed to DeleteArtifact: (404) Not Found" when a delete is retried after a transient server error'
3
+ category: caching-artifacts
4
+ severity: error
5
+ tags:
6
+ - upload-artifact
7
+ - merge
8
+ - delete-merged
9
+ - 404
10
+ - idempotent-delete
11
+ - artifact-merge
12
+ patterns:
13
+ - regex: 'Failed to DeleteArtifact.*non-retryable error.*404|DeleteArtifact.*404.*Not Found'
14
+ flags: 'i'
15
+ - regex: 'Received non-retryable error.*Failed request.*404.*artifact not found'
16
+ flags: 'i'
17
+ error_messages:
18
+ - "Error: Failed to DeleteArtifact: Received non-retryable error: Failed request: (404) Not Found: artifact not found"
19
+ - "Attempt 1 of 5 failed with error: Unexpected token '<', \"<!DOCTYPE \"... is not valid JSON. Retrying request in 3000 ms..."
20
+ root_cause: |
21
+ When using `actions/upload-artifact/merge@v6` (or later) with `delete-merged: true`,
22
+ the action deletes each source artifact after merging them into the combined artifact.
23
+ If a delete request encounters a transient server error (e.g., a malformed HTML response
24
+ instead of JSON — indicated by "Unexpected token '<'"), the action retries the delete.
25
+
26
+ If the first delete request was actually processed by the server before the error was
27
+ returned to the client, the artifact is already gone. The retry then receives a 404
28
+ Not Found, which `DeleteArtifact` treats as a **non-retryable fatal error** and
29
+ immediately aborts the job.
30
+
31
+ Because `delete-merged: true` is destructive — source artifacts have already been
32
+ deleted by this point — retrying the entire job from scratch is impossible, as the
33
+ source artifacts no longer exist.
34
+
35
+ Root bug: `DeleteArtifact` should treat 404 as a success (idempotent delete — the
36
+ artifact not existing IS the desired state). This is tracked in upload-artifact#751
37
+ (open, Jan 2026). No server-side fix has been released as of June 2026.
38
+ fix: |
39
+ Option 1 — Add `continue-on-error: true` to the merge step so that the 404 error
40
+ does not fail the overall job. Verify that the merge artifact was created successfully
41
+ before relying on this workaround, since `continue-on-error` also swallows real
42
+ failures.
43
+
44
+ Option 2 — Set `delete-merged: false` and handle deletion of source artifacts in a
45
+ separate step with explicit error handling (e.g., `gh api` with `--fail-with-body` and
46
+ a conditional step that ignores 404 exit codes).
47
+
48
+ Option 3 — Wrap the merge step in a retry loop using `nick-invision/retry` or a
49
+ shell retry, but note that re-running the merge after partial deletion will fail because
50
+ source artifacts are gone. Prevention is better than recovery here.
51
+
52
+ Monitor upload-artifact#751 for an upstream fix that makes 404 on DeleteArtifact
53
+ idempotent/non-fatal.
54
+ fix_code:
55
+ - language: yaml
56
+ label: 'continue-on-error workaround — prevents 404 from failing the job'
57
+ code: |
58
+ - name: Merge artifacts
59
+ uses: actions/upload-artifact/merge@v6
60
+ # ⚠️ Workaround: continue-on-error so a DeleteArtifact 404 does not fail the job.
61
+ # Also swallows real upload failures — add a downstream step to verify the
62
+ # merged artifact exists if reliability matters.
63
+ continue-on-error: true
64
+ with:
65
+ name: merged-results
66
+ pattern: partial-results-*
67
+ delete-merged: true
68
+ - language: yaml
69
+ label: 'delete-merged: false with manual deletion that ignores 404'
70
+ code: |
71
+ - name: Merge artifacts (no auto-delete)
72
+ uses: actions/upload-artifact/merge@v6
73
+ with:
74
+ name: merged-results
75
+ pattern: partial-results-*
76
+ delete-merged: false # ✅ Suppress automatic deletion
77
+
78
+ # Manually delete source artifacts, ignoring 404 responses (already deleted)
79
+ - name: Delete source artifacts
80
+ env:
81
+ GH_TOKEN: ${{ github.token }}
82
+ run: |
83
+ for artifact_id in $(gh api repos/${{ github.repository }}/actions/artifacts \
84
+ --jq '.artifacts[] | select(.name | startswith("partial-results-")) | .id'); do
85
+ gh api --method DELETE \
86
+ repos/${{ github.repository }}/actions/artifacts/$artifact_id \
87
+ --silent || echo "Artifact $artifact_id already deleted (404) — ignoring"
88
+ done
89
+ prevention:
90
+ - 'Avoid `delete-merged: true` in critical CI pipelines until upload-artifact#751 is fixed — use manual deletion with 404 tolerance instead'
91
+ - 'Add `continue-on-error: true` to merge steps as a temporary workaround for the 404 abort'
92
+ - 'Keep source artifacts available by using `delete-merged: false` until the upstream fix lands'
93
+ - 'Monitor actions/upload-artifact#751 for a release that treats DeleteArtifact 404 as success'
94
+ docs:
95
+ - url: 'https://github.com/actions/upload-artifact/issues/751'
96
+ label: 'upload-artifact#751: DeleteArtifact should not consider a 404 response an error (open, Jan 2026)'
97
+ - url: 'https://github.com/actions/upload-artifact/blob/main/merge/README.md'
98
+ label: 'actions/upload-artifact merge action README — delete-merged option'
99
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/storing-workflow-data-as-artifacts'
100
+ label: 'GitHub Docs: Storing workflow data as artifacts'
@@ -0,0 +1,95 @@
1
+ id: caching-artifacts-079
2
+ title: '`setup-node@v5` auto-enables caching when `packageManager` is set in `package.json` — post-run fails with "Path Validation Error" if dependencies are not installed'
3
+ category: caching-artifacts
4
+ severity: error
5
+ tags:
6
+ - setup-node
7
+ - caching
8
+ - packageManager
9
+ - path-validation
10
+ - post-run
11
+ - setup-node-v5
12
+ - automatic-caching
13
+ patterns:
14
+ - regex: 'Path Validation Error.*Path.*specified.*action.*caching.*do.*not exist'
15
+ flags: 'i'
16
+ - regex: 'Path.*specified.*action.*caching.*do.{0,5}not exist.*no cache'
17
+ flags: 'i'
18
+ error_messages:
19
+ - "Error: Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved."
20
+ root_cause: |
21
+ Starting with `actions/setup-node@v5`, automatic caching is enabled **by default**
22
+ whenever a `packageManager` field is present in the project's `package.json` file —
23
+ even if the workflow does not explicitly configure caching via the `cache:` input.
24
+
25
+ This means jobs that merely run linting, type-checking, or any step that does not
26
+ execute `npm install` / `yarn install` / `pnpm install` will still attempt to cache
27
+ the package manager's dependency directory in the post-run cleanup step. Because no
28
+ `install` was run, the expected cache paths (e.g., `~/.npm`, `~/.yarn/cache`,
29
+ `~/.pnpm-store`) do not exist. The toolkit's path validation then emits:
30
+ "Path Validation Error: Path(s) specified in the action for caching do(es) not exist"
31
+ and **fails the job step**, turning a previously green run red.
32
+
33
+ **Why this surprises developers:**
34
+ - The workflow never explicitly enables caching — the presence of `packageManager`
35
+ in `package.json` is the invisible trigger.
36
+ - The error appears in the **post-run** cleanup step, not in the setup step where
37
+ caching is configured, making it harder to trace.
38
+ - Workflows that have always worked (no `cache:` input) start failing after upgrading
39
+ from `setup-node@v4` to `setup-node@v5`.
40
+
41
+ **Resolution:** `setup-node@v6` (released Oct 2025) changed the default so automatic
42
+ caching only applies to npm; yarn/pnpm caching is now opt-in. Upgrading to v6 or
43
+ explicitly disabling the cache in v5 resolves the issue.
44
+ Source: setup-node#1363 (5 reactions, closed Oct 2025 via v6 release).
45
+ fix: |
46
+ Option 1 (recommended) — Upgrade to `actions/setup-node@v6`. The v6 release limits
47
+ automatic caching to npm only; yarn and pnpm caching require explicit `cache: yarn`
48
+ or `cache: pnpm` input.
49
+
50
+ Option 2 — Disable automatic caching in v5 by setting `package-manager-cache: false`.
51
+ Use this if you cannot upgrade to v6 immediately.
52
+
53
+ Option 3 — Ensure dependencies are installed in every job that uses `setup-node@v5`.
54
+ If the job only needs Node for tooling (lint, type-check, etc.), run a minimal
55
+ `npm ci --ignore-scripts` or equivalent before the setup post-run fires.
56
+ fix_code:
57
+ - language: yaml
58
+ label: 'Upgrade to setup-node@v6 — fixes automatic caching defaults'
59
+ code: |
60
+ # ✅ Recommended: v6 only auto-caches npm, not yarn/pnpm
61
+ - uses: actions/setup-node@v6
62
+ with:
63
+ node-version: '22'
64
+ # No cache: input needed — auto-caching is npm-only in v6
65
+ - language: yaml
66
+ label: 'Disable automatic caching in v5 with package-manager-cache: false'
67
+ code: |
68
+ # ✅ Workaround for setup-node@v5: suppress auto-caching
69
+ - uses: actions/setup-node@v5
70
+ with:
71
+ node-version: '22'
72
+ package-manager-cache: false # Disables packageManager-triggered auto-cache
73
+ - run: npx eslint src/
74
+ - language: yaml
75
+ label: 'Opt in to caching explicitly for jobs that do install dependencies'
76
+ code: |
77
+ # ✅ Jobs that do install: use explicit cache input instead of relying on auto-detect
78
+ - uses: actions/setup-node@v6
79
+ with:
80
+ node-version: '22'
81
+ cache: 'npm' # Explicit opt-in — cache only when deps are installed
82
+ - run: npm ci
83
+ - run: npm test
84
+ prevention:
85
+ - 'Upgrade to `setup-node@v6` which fixes the over-eager auto-caching of yarn/pnpm in v5'
86
+ - 'In v5, always set `package-manager-cache: false` for jobs that do not run `install`'
87
+ - 'Check `package.json` for a `packageManager` field — its presence triggers auto-caching in v5 even without an explicit `cache:` input'
88
+ - 'Run lint, type-check, and audit jobs without setup-node caching to keep them fast and avoid phantom cache failures'
89
+ docs:
90
+ - url: 'https://github.com/actions/setup-node/issues/1363'
91
+ label: 'setup-node#1363: v5 fails with Path Validation Error in Post Run steps (5 reactions, resolved in v6)'
92
+ - url: 'https://github.com/actions/setup-node/releases/tag/v6.0.0'
93
+ label: 'setup-node v6.0.0 release notes — auto-caching limited to npm'
94
+ - url: 'https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-data'
95
+ label: 'setup-node docs: Caching packages data'
@@ -0,0 +1,140 @@
1
+ id: known-unsolved-077
2
+ title: '`fromJSON()` array nested inside a matrix object does not expand — resolves as literal "Array" or raw JSON string'
3
+ category: known-unsolved
4
+ severity: silent-failure
5
+ tags:
6
+ - matrix
7
+ - fromJSON
8
+ - dynamic-matrix
9
+ - object
10
+ - array-expansion
11
+ - known-limitation
12
+ - silent-failure
13
+ patterns:
14
+ - regex: 'fromJSON\(needs\.\w+\.outputs\.'
15
+ flags: 'i'
16
+ - regex: 'matrix\.\w+\.\w+.*\bArray\b|\bArray\b.*matrix\.'
17
+ flags: 'i'
18
+ error_messages:
19
+ - "echo x86_64 Array"
20
+ - 'matrix.component.distro = Array'
21
+ - 'matrix.component.distro = ["el8","el9"]'
22
+ root_cause: |
23
+ GitHub Actions supports expanding a JSON array into matrix rows when `fromJSON()` is
24
+ used **at the top level** of a matrix dimension:
25
+ ```yaml
26
+ strategy:
27
+ matrix:
28
+ version: ${{ fromJSON(needs.setup.outputs.versions) }} # ✅ expands to N rows
29
+ ```
30
+
31
+ However, when the array is nested inside a matrix **object** (i.e., as a value within
32
+ one of the items of a matrix dimension), `fromJSON()` does NOT expand it into rows.
33
+ Instead, the expression resolves to:
34
+ - The literal string `"Array"` when `fromJSON()` is used (type-1 pattern)
35
+ - The raw JSON string (e.g., `["el8","el9"]`) when the output is used directly (type-2 pattern)
36
+
37
+ **Failing patterns:**
38
+ ```yaml
39
+ # Type 1 — fromJSON inside a matrix object value
40
+ matrix:
41
+ component:
42
+ - name: rhel
43
+ distro: ${{ fromJSON(needs.setup.outputs.rpms) }} # ❌ becomes "Array"
44
+
45
+ # Type 2 — raw JSON string inside a matrix object value
46
+ matrix:
47
+ component:
48
+ - name: rhel
49
+ distro: ${{ needs.setup.outputs.rpms }} # ❌ becomes the raw JSON string
50
+ ```
51
+
52
+ The expansion mechanism only applies when the entire matrix dimension value is a
53
+ `fromJSON()` call returning an array. Nesting breaks the expansion because the runner
54
+ evaluates the object first and cannot retroactively fan out a single object item into
55
+ multiple rows.
56
+
57
+ This is a documented limitation. GitHub's matrix engine does not support nested array
58
+ expansion within object-typed matrix dimensions. The issue is tracked in
59
+ actions/runner#3794 (open since Apr 2025, no planned fix).
60
+
61
+ **Why this is confusing:**
62
+ The `fromJSON()` approach works perfectly when the array IS the entire dimension:
63
+ `matrix: version: ${{ fromJSON(needs.setup.outputs.versions) }}`. Developers naturally
64
+ try to reuse the same pattern inside object dimensions and get silently wrong values —
65
+ the job runs to completion but with a single row containing "Array" instead of N rows.
66
+ fix: |
67
+ Restructure the matrix so that the dynamic array is the TOP-LEVEL matrix dimension,
68
+ not nested inside an object. Move any additional object properties into the
69
+ `matrix.include` block to pair them with each array element.
70
+
71
+ If you need multiple outputs to compose a matrix, pre-process them into a combined
72
+ JSON array in an upstream job step and pass the combined output as a single
73
+ `fromJSON()` expression.
74
+ fix_code:
75
+ - language: yaml
76
+ label: 'Move the dynamic array to the top-level dimension — this expands correctly'
77
+ code: |
78
+ jobs:
79
+ setup:
80
+ runs-on: ubuntu-latest
81
+ outputs:
82
+ rpms: ${{ steps.distros.outputs.rpms }}
83
+ debs: ${{ steps.distros.outputs.debs }}
84
+ steps:
85
+ - id: distros
86
+ run: |
87
+ echo 'rpms=["el8","el9"]' >> "$GITHUB_OUTPUT"
88
+ echo 'debs=["focal","jammy","noble"]' >> "$GITHUB_OUTPUT"
89
+
90
+ build:
91
+ needs: setup
92
+ runs-on: ubuntu-latest
93
+ strategy:
94
+ matrix:
95
+ # ✅ distro is the top-level dimension — fromJSON expands to N rows
96
+ distro: ${{ fromJSON(needs.setup.outputs.rpms) }}
97
+ steps:
98
+ - run: echo "Building for ${{ matrix.distro }}"
99
+ - language: yaml
100
+ label: 'Pre-combine multiple arrays into a single include list in the upstream job'
101
+ code: |
102
+ jobs:
103
+ setup:
104
+ runs-on: ubuntu-latest
105
+ outputs:
106
+ matrix: ${{ steps.build-matrix.outputs.matrix }}
107
+ steps:
108
+ - id: build-matrix
109
+ run: |
110
+ # Build a combined include list from multiple arrays
111
+ matrix=$(python3 -c "
112
+ import json, sys
113
+ rpms = ['el8', 'el9']
114
+ debs = ['focal', 'jammy', 'noble']
115
+ include = [{'family': 'rhel', 'distro': d} for d in rpms] + \
116
+ [{'family': 'debian', 'distro': d} for d in debs]
117
+ print(json.dumps({'include': include}))
118
+ ")
119
+ echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
120
+
121
+ build:
122
+ needs: setup
123
+ runs-on: ubuntu-latest
124
+ strategy:
125
+ # ✅ fromJSON on a single combined matrix object with include list
126
+ matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
127
+ steps:
128
+ - run: echo "Building ${{ matrix.family }} for ${{ matrix.distro }}"
129
+ prevention:
130
+ - 'Never use `fromJSON()` as the value of a property inside a matrix object — it will not expand to multiple rows'
131
+ - 'Only use `fromJSON()` when the array IS the entire dimension value (e.g., `matrix.version: ${{ fromJSON(...) }}`)'
132
+ - 'When combining multi-dimensional dynamic matrices, pre-compute a combined `include` list in an upstream job'
133
+ - 'Verify matrix expansion by adding a `- run: echo ${{ toJSON(matrix) }}` debug step; "Array" in the output confirms the bug'
134
+ docs:
135
+ - url: 'https://github.com/actions/runner/issues/3794'
136
+ label: 'actions/runner#3794: Array outputs not understood by matrix when nested inside object (open, Apr 2025)'
137
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow#using-a-matrix-strategy'
138
+ label: 'GitHub Docs: Using a matrix strategy'
139
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#fromjson'
140
+ label: 'GitHub Docs: fromJSON expression function'
@@ -0,0 +1,120 @@
1
+ id: runner-environment-246
2
+ title: 'Container `options:` empty string from matrix causes "The template is not valid. Unexpected value''" in runner >= 2.331.0'
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - container
7
+ - matrix
8
+ - container-options
9
+ - runner-regression
10
+ - template-validation
11
+ - runner-2331
12
+ patterns:
13
+ - regex: 'The template is not valid.*Unexpected value'
14
+ flags: 'i'
15
+ - regex: 'Unexpected value .{0,5}\.github/workflows'
16
+ flags: 'i'
17
+ error_messages:
18
+ - "Error: The template is not valid. .github/workflows/deploy.yml (Line: 42, Col: 15): Unexpected value ''"
19
+ - "Error: The template is not valid. .github/workflows/ci.yml (Line: 18, Col: 9): Unexpected value ''"
20
+ root_cause: |
21
+ In runner version 2.331.0, a regression was introduced in template validation for
22
+ container job `options:` fields. When a workflow uses a matrix to conditionally set
23
+ container options — so that some matrix combinations have no container options
24
+ (`options: ''` or `options: ${{ matrix.container_options }}` where the value is
25
+ unset/empty) — the runner's template evaluator now rejects the empty string with
26
+ "Unexpected value ''" and marks the job as failed before it even starts.
27
+
28
+ Prior to runner 2.331.0, an empty `options:` string was silently accepted and the
29
+ container job ran without extra Docker options. The 2.331.0 release tightened template
30
+ validation for the `container.options` field, rejecting any expression that evaluates
31
+ to an empty string at runtime.
32
+
33
+ **Common trigger pattern:**
34
+ ```yaml
35
+ strategy:
36
+ matrix:
37
+ include:
38
+ - os: ubuntu
39
+ container_options: "--memory=2g"
40
+ - os: alpine
41
+ # No container_options key — evaluates to empty string
42
+ jobs:
43
+ build:
44
+ container:
45
+ image: myapp:latest
46
+ options: ${{ matrix.container_options }} # empty for alpine row
47
+ ```
48
+
49
+ This pattern worked on runner 2.330.0 but fails on 2.331.0+ with "Unexpected value ''".
50
+ The issue is tracked in actions/runner#4204 (open, labeled bug, Jan 2026).
51
+ fix: |
52
+ Add a fallback non-empty value to the options expression using the `||` operator.
53
+ A single space `' '` is enough to satisfy the validator while having no effect on
54
+ the container invocation, since Docker ignores empty/whitespace-only option strings.
55
+
56
+ Replace:
57
+ options: ${{ matrix.container_options }}
58
+
59
+ With:
60
+ options: ${{ matrix.container_options || ' ' }}
61
+
62
+ Alternatively, restructure the matrix to always provide a defined (non-empty)
63
+ `container_options` value, using a neutral default like `'--label=placeholder'`
64
+ for rows that don't need real options. This is more explicit and survives future
65
+ validator changes.
66
+ fix_code:
67
+ - language: yaml
68
+ label: 'Fallback to single space so template validator accepts an empty matrix value'
69
+ code: |
70
+ strategy:
71
+ matrix:
72
+ include:
73
+ - name: with-limits
74
+ container_options: "--memory=2g --cpus=1"
75
+ - name: no-limits
76
+ # container_options intentionally absent
77
+
78
+ jobs:
79
+ build:
80
+ runs-on: ubuntu-latest
81
+ container:
82
+ image: myapp:latest
83
+ # ✅ Use || ' ' to avoid empty-string rejection in runner >= 2.331.0
84
+ options: ${{ matrix.container_options || ' ' }}
85
+ steps:
86
+ - uses: actions/checkout@v4
87
+ - run: make build
88
+ - language: yaml
89
+ label: 'Explicit neutral default in the matrix row avoids the ambiguity entirely'
90
+ code: |
91
+ strategy:
92
+ matrix:
93
+ include:
94
+ - name: with-limits
95
+ container_options: "--memory=2g --cpus=1"
96
+ - name: no-limits
97
+ # ✅ Always set a value — Docker ignores a dummy label at no cost
98
+ container_options: "--label=no-extra-opts"
99
+
100
+ jobs:
101
+ build:
102
+ runs-on: ubuntu-latest
103
+ container:
104
+ image: myapp:latest
105
+ options: ${{ matrix.container_options }}
106
+ steps:
107
+ - uses: actions/checkout@v4
108
+ - run: make build
109
+ prevention:
110
+ - 'Never let `container.options` evaluate to an empty string — always provide a neutral fallback with `|| '' ''` or a dummy label'
111
+ - 'Test matrix workflows with every combination, including rows that skip optional matrix keys like `container_options`'
112
+ - 'Pin runner version in self-hosted setups to detect regressions before rolling out to all jobs'
113
+ - 'Track actions/runner#4204 for a permanent fix; apply the `|| '' ''` workaround in the interim'
114
+ docs:
115
+ - url: 'https://github.com/actions/runner/issues/4204'
116
+ label: 'actions/runner#4204: "The template is not valid" when container.options is not set in matrix (open, regression in 2.331.0)'
117
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-jobs-in-a-container'
118
+ label: 'GitHub Docs: Running jobs in a container — options field'
119
+ - url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idcontaineroptions'
120
+ label: 'GitHub Docs: jobs.<job_id>.container.options'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@htekdev/actions-debugger",
3
- "version": "1.0.132",
3
+ "version": "1.0.133",
4
4
  "description": "65+ real GitHub Actions errors, queryable by agents. CLI + MCP server + Copilot skills + error database.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",