@htekdev/actions-debugger 1.0.14 → 1.0.16

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.
Files changed (58) hide show
  1. package/dist/db/search.js +3 -1
  2. package/dist/db/search.js.map +1 -1
  3. package/dist/tools/suggest-fix.d.ts.map +1 -1
  4. package/dist/tools/suggest-fix.js +5 -1
  5. package/dist/tools/suggest-fix.js.map +1 -1
  6. package/errors/caching-artifacts/cache-key-too-long.yml +93 -0
  7. package/errors/caching-artifacts/cache-path-not-exist-skipped.yml +152 -0
  8. package/errors/caching-artifacts/cache-save-same-key-html-conflict.yml +109 -0
  9. package/errors/caching-artifacts/docker-buildx-gha-cache-capacity.yml +107 -0
  10. package/errors/caching-artifacts/setup-ruby-bundler-ephemeral-workdir-cache-miss.yml +147 -0
  11. package/errors/caching-artifacts/upload-artifact-v3-retirement-blocked.yml +123 -0
  12. package/errors/caching-artifacts/upload-artifact-v4-large-file-macos-hang.yml +111 -0
  13. package/errors/concurrency-timing/always-cleanup-5min-forced-kill.yml +140 -0
  14. package/errors/concurrency-timing/concurrency-group-env-context-undefined.yml +99 -0
  15. package/errors/concurrency-timing/required-check-pending-path-filter-skip.yml +160 -0
  16. package/errors/concurrency-timing/wait-timer-cancel-in-progress-starvation.yml +125 -0
  17. package/errors/known-unsolved/composite-action-step-timeout-minutes-ignored.yml +146 -0
  18. package/errors/known-unsolved/reusable-workflow-no-composite-action-call.yml +116 -0
  19. package/errors/known-unsolved/schedule-trigger-default-branch-only.yml +113 -0
  20. package/errors/known-unsolved/secrets-not-allowed-in-if-conditions.yml +149 -0
  21. package/errors/known-unsolved/workflow-50-rerun-limit.yml +110 -0
  22. package/errors/permissions-auth/check-run-status-modification-blocked.yml +134 -0
  23. package/errors/permissions-auth/dependabot-pr-secrets-unavailable.yml +133 -0
  24. package/errors/permissions-auth/fine-grained-pat-deployment-write-required.yml +146 -0
  25. package/errors/permissions-auth/github-app-installation-token-new-format.yml +124 -0
  26. package/errors/permissions-auth/github-packages-read-requires-packages-permission.yml +128 -0
  27. package/errors/permissions-auth/oidc-id-token-write-permission-missing.yml +169 -0
  28. package/errors/permissions-auth/permissions-empty-block-removes-contents-read.yml +97 -0
  29. package/errors/permissions-auth/reusable-workflow-permissions-not-inherited.yml +114 -0
  30. package/errors/runner-environment/checkout-windows-ebusy-lock.yml +124 -0
  31. package/errors/runner-environment/deprecated-action-version-auto-rejected.yml +89 -0
  32. package/errors/runner-environment/github-hosted-runner-disk-space-full.yml +85 -0
  33. package/errors/runner-environment/github-path-same-step-not-found.yml +114 -0
  34. package/errors/runner-environment/github-script-v6-octokit-rest-actions-not-function.yml +87 -0
  35. package/errors/runner-environment/macos-13-deprecation-brownout.yml +93 -0
  36. package/errors/runner-environment/macos-15-mono-nuget-removed.yml +151 -0
  37. package/errors/runner-environment/macos-15-xcode-simulator-sdk-policy.yml +141 -0
  38. package/errors/runner-environment/multi-runtime-nov2025-removal.yml +120 -0
  39. package/errors/runner-environment/runner-oom-exit-code-137.yml +117 -0
  40. package/errors/runner-environment/setup-go-go123-telemetry-cache-failure.yml +92 -0
  41. package/errors/runner-environment/setup-java-distribution-required.yml +108 -0
  42. package/errors/runner-environment/ubuntu-2004-retirement-brownout.yml +107 -0
  43. package/errors/runner-environment/windows-latest-d-drive-removed.yml +104 -0
  44. package/errors/runner-environment/windows-vs2026-cuda-host-compiler-unsupported.yml +145 -0
  45. package/errors/silent-failures/event-commits-empty-on-workflow-dispatch.yml +110 -0
  46. package/errors/silent-failures/fetch-tags-depth-one-silent-no-op.yml +77 -0
  47. package/errors/silent-failures/github-env-multiline-value-truncated.yml +127 -0
  48. package/errors/silent-failures/github-sha-pr-merge-commit-not-head.yml +150 -0
  49. package/errors/silent-failures/job-output-masked-as-secret-empty.yml +147 -0
  50. package/errors/silent-failures/upload-artifact-permissions-stripped.yml +98 -0
  51. package/errors/triggers/pull-request-branches-filter-matches-base-not-head.yml +140 -0
  52. package/errors/triggers/push-event-fires-on-branch-delete.yml +129 -0
  53. package/errors/triggers/push-first-commit-before-sha-zeros.yml +160 -0
  54. package/errors/yaml-syntax/continue-on-error-env-context-rejected.yml +130 -0
  55. package/errors/yaml-syntax/fromjson-empty-string-crash.yml +99 -0
  56. package/errors/yaml-syntax/if-bang-negation-yaml-tag.yml +145 -0
  57. package/errors/yaml-syntax/local-action-path-always-top-level.yml +142 -0
  58. package/package.json +1 -1
@@ -0,0 +1,129 @@
1
+ id: triggers-015
2
+ title: "on: push Fires When a Branch Is Deleted — Unguarded Workflows Fail"
3
+ category: triggers
4
+ severity: error
5
+ tags:
6
+ - push
7
+ - branch-delete
8
+ - github.event.deleted
9
+ - ref-not-found
10
+ - workflow-trigger
11
+ - branch-cleanup
12
+ patterns:
13
+ - regex: "on:\\s*push"
14
+ flags: "im"
15
+ - regex: "github\\.event\\.deleted"
16
+ flags: "i"
17
+ - regex: "fatal.*not.*a.*git repository|fatal.*couldn't find remote ref"
18
+ flags: "i"
19
+ error_messages:
20
+ - "fatal: couldn't find remote ref refs/heads/my-feature-branch"
21
+ - "Error: The process '/usr/bin/git' failed with exit code 128"
22
+ - "fatal: not a git repository (or any of the parent directories): .git"
23
+ - "remote: Repository not found"
24
+ root_cause: |
25
+ GitHub's `push` webhook fires on ALL ref write operations, including branch and tag
26
+ deletions. When a branch is deleted, a push event is emitted with:
27
+ - `github.event.deleted == true`
28
+ - `github.event.created == false`
29
+ - `github.ref` set to the deleted branch ref (e.g., `refs/heads/feature-xyz`)
30
+ - `github.event.after` set to `0000000000000000000000000000000000000000` (40 zeros)
31
+ - `github.sha` reverts to the default branch SHA
32
+
33
+ Workflows triggered by `on: push` that do not guard against deletion events can:
34
+ - Fail when `actions/checkout` tries to check out a branch that no longer exists
35
+ - Trigger deployment pipelines, test suites, or notification workflows unexpectedly
36
+ - Produce misleading failures that look like unrelated CI breakage
37
+
38
+ This is especially problematic in workflows that:
39
+ - Checkout a specific branch ref from `github.ref`
40
+ - Run clean-up scripts expecting the branch to exist
41
+ - Use `git describe` or `git log` on the tip of the deleted branch
42
+
43
+ GitHub documentation notes: "When you delete a branch, the SHA in the workflow run
44
+ (and its associated refs) reverts to the default branch of the repository."
45
+ Source: GitHub Docs — Events that trigger workflows (push event payload)
46
+ Source: GitHub Docs — Webhook events and payloads (`deleted` field on push payload)
47
+ fix: |
48
+ Add an `if:` guard to any job or workflow step that should not run on branch deletions.
49
+ The `github.event.deleted` context field is `true` when the push event is a deletion.
50
+
51
+ For most push-triggered workflows, the intent is to react to new commits — guard with:
52
+ `if: github.event.deleted != true`
53
+
54
+ For tag deletion events, also check `github.event.ref_type` if you use the `delete`
55
+ event, or use `if: github.event.deleted != true && !startsWith(github.ref, 'refs/tags/')`.
56
+
57
+ Alternatively, use the `on: delete` event for branch/tag cleanup workflows instead of
58
+ listening for deletions via the push event.
59
+ fix_code:
60
+ - language: yaml
61
+ label: "Guard push jobs from running on branch deletion"
62
+ code: |
63
+ on:
64
+ push:
65
+ branches:
66
+ - 'feature/**'
67
+ - 'fix/**'
68
+
69
+ jobs:
70
+ build:
71
+ runs-on: ubuntu-latest
72
+ # ✅ Skip the job entirely when the push is a branch deletion
73
+ if: github.event.deleted != true
74
+ steps:
75
+ - uses: actions/checkout@v4
76
+ - run: npm ci && npm test
77
+
78
+ - language: yaml
79
+ label: "Per-step guard if you need some steps to run on deletion"
80
+ code: |
81
+ jobs:
82
+ process:
83
+ runs-on: ubuntu-latest
84
+ steps:
85
+ - name: Checkout (skip on delete)
86
+ if: github.event.deleted != true
87
+ uses: actions/checkout@v4
88
+
89
+ - name: Run tests (skip on delete)
90
+ if: github.event.deleted != true
91
+ run: npm test
92
+
93
+ - name: Log branch deleted
94
+ if: github.event.deleted == true
95
+ run: echo "Branch ${{ github.ref_name }} was deleted"
96
+
97
+ - language: yaml
98
+ label: "Use on: delete event for cleanup workflows instead"
99
+ code: |
100
+ # Prefer the dedicated 'delete' event for branch/tag cleanup
101
+ on:
102
+ delete:
103
+ # Optionally filter to branches only (not tags)
104
+
105
+ jobs:
106
+ cleanup:
107
+ runs-on: ubuntu-latest
108
+ # github.event.ref is the deleted branch/tag name
109
+ # github.event.ref_type is 'branch' or 'tag'
110
+ if: github.event.ref_type == 'branch'
111
+ steps:
112
+ - name: Clean up preview environment
113
+ run: |
114
+ echo "Cleaning up for deleted branch: ${{ github.event.ref }}"
115
+ ./scripts/teardown-preview.sh "${{ github.event.ref }}"
116
+
117
+ prevention:
118
+ - "Add `if: github.event.deleted != true` to all jobs in `on: push` workflows."
119
+ - "Use the `on: delete` event for branch/tag teardown workflows instead of catching deletions via push."
120
+ - "Check `github.event.after == '0000000000000000000000000000000000000000'` as an alternative deletion guard."
121
+ - "Avoid checking out `${{ github.ref }}` directly; use `actions/checkout` defaults which handle this gracefully."
122
+ - "Add branch filters under `on: push: branches:` to limit which branches trigger the workflow."
123
+ docs:
124
+ - url: "https://docs.github.com/en/webhooks/webhook-events-and-payloads#push"
125
+ label: "GitHub Docs: push webhook event payload (deleted field)"
126
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push"
127
+ label: "GitHub Docs: push event trigger"
128
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#delete"
129
+ label: "GitHub Docs: delete event trigger"
@@ -0,0 +1,160 @@
1
+ id: triggers-014
2
+ title: "github.event.before Is All Zeros on First Push to a New Branch — git diff Fails"
3
+ category: triggers
4
+ severity: error
5
+ tags:
6
+ - push
7
+ - first-push
8
+ - git-diff
9
+ - before-sha
10
+ - new-branch
11
+ - branch-creation
12
+ patterns:
13
+ - regex: "fatal.*bad revision.*0000000000000000000000000000000000000000"
14
+ flags: "i"
15
+ - regex: "fatal.*ambiguous argument '0{40}'"
16
+ flags: "i"
17
+ - regex: "unknown revision or path not in the working tree.*0{10}"
18
+ flags: "i"
19
+ - regex: "0{40}\\.\\.\\.\\$\\{\\{.*github\\.event\\.before"
20
+ flags: "i"
21
+ error_messages:
22
+ - "fatal: bad revision '0000000000000000000000000000000000000000...HEAD'"
23
+ - "fatal: ambiguous argument '0000000000000000000000000000000000000000': unknown revision or path not in the working tree"
24
+ - "error: object file .git/objects/00/00000000000000000000000000000000000000 is empty"
25
+ root_cause: |
26
+ When a **new branch is pushed for the first time**, `github.event.before` is set to
27
+ `0000000000000000000000000000000000000000` (40 zeros), representing the null/empty SHA.
28
+ This indicates there was no previous commit on this branch — it is a branch creation event.
29
+
30
+ Workflows that use `github.event.before` to compute a diff range (e.g., to find changed
31
+ files) fail when run on a first push because `git diff` or `git log` cannot resolve the
32
+ null SHA:
33
+
34
+ ```yaml
35
+ run: git diff ${{ github.event.before }}...HEAD
36
+ # On first push: git diff 0000000000000000000000000000000000000000...HEAD → FATAL ERROR
37
+ ```
38
+
39
+ **When this occurs:**
40
+ - First push to a new branch (branch creation)
41
+ - `push` events that create a new branch (`github.event.created == true`)
42
+ - Forced pushes that reference a non-existent before-SHA (rare)
43
+
44
+ **Why it's painful:**
45
+ - Workflows work fine for subsequent pushes (where `before` is a real commit)
46
+ - The error only appears on the first push, often during branch creation in a PR workflow
47
+ - The error message references the git object store internals, not the workflow context
48
+ - Developers check out the diff command syntax without realizing the SHA is invalid
49
+ fix: |
50
+ **Option 1 (recommended): Guard against the null SHA before running git diff**
51
+
52
+ Check if `github.event.before` is the null SHA and fall back to diffing against the
53
+ merge base or the initial commit.
54
+
55
+ **Option 2: Use the `dorny/paths-filter` action**
56
+
57
+ The `dorny/paths-filter` action handles first-push, force-push, and all edge cases
58
+ in branch diff computation automatically.
59
+
60
+ **Option 3: Use `github.event.created` to skip diff-based logic on branch creation**
61
+
62
+ Add an `if: github.event.created != true` guard to diff-dependent steps so they only
63
+ run on non-creation push events.
64
+ fix_code:
65
+ - language: yaml
66
+ label: "Broken — git diff with github.event.before fails on first push"
67
+ code: |
68
+ # ❌ BROKEN: Fails when branch is pushed for the first time
69
+ on: push
70
+
71
+ jobs:
72
+ lint-changed:
73
+ runs-on: ubuntu-latest
74
+ steps:
75
+ - uses: actions/checkout@v4
76
+ with:
77
+ fetch-depth: 0
78
+ - name: Lint only changed files
79
+ run: |
80
+ CHANGED=$(git diff --name-only ${{ github.event.before }}...HEAD)
81
+ # FATAL on first push: 0000000000000000000000000000000000000000 is not a commit
82
+ - language: yaml
83
+ label: "Fixed — guard against null SHA before git diff"
84
+ code: |
85
+ # ✅ FIXED: Check for null SHA and fall back to first commit
86
+ on: push
87
+
88
+ jobs:
89
+ lint-changed:
90
+ runs-on: ubuntu-latest
91
+ steps:
92
+ - uses: actions/checkout@v4
93
+ with:
94
+ fetch-depth: 0
95
+ - name: Lint only changed files
96
+ run: |
97
+ BEFORE="${{ github.event.before }}"
98
+ NULL_SHA="0000000000000000000000000000000000000000"
99
+
100
+ if [ "$BEFORE" = "$NULL_SHA" ]; then
101
+ # First push to new branch — diff against the initial commit or all files
102
+ echo "First push to branch, diffing all files"
103
+ CHANGED=$(git diff --name-only $(git rev-list --max-parents=0 HEAD)...HEAD)
104
+ else
105
+ CHANGED=$(git diff --name-only "$BEFORE"...HEAD)
106
+ fi
107
+ echo "$CHANGED"
108
+ - language: yaml
109
+ label: "Fixed — skip diff steps on branch creation using github.event.created"
110
+ code: |
111
+ # ✅ FIXED: Skip diff-based steps entirely on first push (branch creation)
112
+ on: push
113
+
114
+ jobs:
115
+ lint-changed:
116
+ runs-on: ubuntu-latest
117
+ steps:
118
+ - uses: actions/checkout@v4
119
+ with:
120
+ fetch-depth: 0
121
+ - name: Lint only changed files
122
+ # Skip on branch creation (before SHA is null)
123
+ if: github.event.created != true
124
+ run: |
125
+ git diff --name-only ${{ github.event.before }}...HEAD | xargs eslint
126
+ - language: yaml
127
+ label: "Fixed — use dorny/paths-filter which handles null SHA automatically"
128
+ code: |
129
+ # ✅ RECOMMENDED: dorny/paths-filter handles first-push and force-push edge cases
130
+ on: push
131
+
132
+ jobs:
133
+ lint-changed:
134
+ runs-on: ubuntu-latest
135
+ steps:
136
+ - uses: actions/checkout@v4
137
+ - uses: dorny/paths-filter@v3
138
+ id: changes
139
+ with:
140
+ filters: |
141
+ src:
142
+ - 'src/**'
143
+ tests:
144
+ - 'tests/**'
145
+ - name: Lint src changes
146
+ if: steps.changes.outputs.src == 'true'
147
+ run: npm run lint
148
+ prevention:
149
+ - "Never use `git diff ${{ github.event.before }}` without guarding against the null SHA (`0000000000000000000000000000000000000000`) — this is guaranteed to fail on first push."
150
+ - "Prefer `dorny/paths-filter` for changed-file detection — it handles all SHA edge cases (first push, force push, merge commits) internally."
151
+ - "If you must use raw git diff, always add: `if [ \"$BEFORE\" = \"$NULL_SHA\" ]; then echo 'first push'; exit 0; fi`."
152
+ - "Test your diff workflows by creating a fresh test branch — don't only test on subsequent pushes where `before` is always valid."
153
+ - "Use `github.event.created == true` in `if:` conditions to identify branch creation events and handle them separately."
154
+ docs:
155
+ - url: "https://docs.github.com/en/webhooks/webhook-events-and-payloads#push"
156
+ label: "GitHub Docs: push webhook event payload — before field"
157
+ - url: "https://github.com/dorny/paths-filter"
158
+ label: "dorny/paths-filter — changed file detection with edge case handling"
159
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push"
160
+ label: "GitHub Docs: Events that trigger workflows — push"
@@ -0,0 +1,130 @@
1
+ id: yaml-syntax-022
2
+ title: "env Context Rejected in continue-on-error and Other Job-Level Boolean Attributes"
3
+ category: yaml-syntax
4
+ severity: error
5
+ tags:
6
+ - continue-on-error
7
+ - env-context
8
+ - expression
9
+ - job-level
10
+ - unrecognized-named-value
11
+ - context-availability
12
+ patterns:
13
+ - regex: "Unrecognized named-value: 'env'"
14
+ flags: "i"
15
+ - regex: "Unexpected value.*env\\."
16
+ flags: "i"
17
+ - regex: "continue-on-error.*env\\.|env\\..*continue-on-error"
18
+ flags: "i"
19
+ - regex: "The workflow is not valid.*Unrecognized named-value: 'env'"
20
+ flags: "i"
21
+ error_messages:
22
+ - "The workflow is not valid. .github/workflows/ci.yml (Line X, Col Y): Unrecognized named-value: 'env'. Located at position 1 within expression: env.MY_FLAG"
23
+ - "Unexpected value '${{ contains(env.MY_LIST, matrix.value) }}'"
24
+ root_cause: |
25
+ The `env` context is NOT available in all workflow attribute positions. Specifically,
26
+ job-level and step-level attributes that accept boolean values or simple flags — such as
27
+ `continue-on-error`, `timeout-minutes` (value expressions), and similar fields — do not
28
+ support the `env` context in their expression evaluations.
29
+
30
+ This is a known GitHub Actions platform limitation documented in actions/runner#1492. The
31
+ error occurs because these attributes are evaluated at workflow parse/validation time (before
32
+ the job runs), when environment variables have not yet been set in the runner context.
33
+
34
+ Contexts available in `continue-on-error`:
35
+ - ✅ `github`, `needs`, `strategy`, `matrix`
36
+ - ❌ `env`, `secrets`, `steps`, `jobs`
37
+
38
+ A common pattern that fails:
39
+ ```yaml
40
+ steps:
41
+ - run: ./build.sh
42
+ continue-on-error: ${{ contains(env.SUPPORTED_VERSIONS, matrix.version) }}
43
+ ```
44
+
45
+ Even if `env.SUPPORTED_VERSIONS` was set in the `env:` block at the top of the workflow,
46
+ the expression fails because `env` is not a supported context in that attribute.
47
+ fix: |
48
+ Move the boolean logic into a step output computed earlier in the job, then reference the
49
+ step output in `continue-on-error`. Alternatively, restructure the workflow to avoid needing
50
+ `env`-based expressions in boolean job/step attributes.
51
+
52
+ **Option 1**: Compute the flag in an earlier step and use `steps` context:
53
+ ```yaml
54
+ - id: check
55
+ run: echo "should_skip=$([[ "$MY_FLAG" == "true" ]] && echo true || echo false)" >> $GITHUB_OUTPUT
56
+ - run: ./potentially-failing-task.sh
57
+ continue-on-error: ${{ steps.check.outputs.should_skip == 'true' }}
58
+ ```
59
+
60
+ **Option 2**: Use `matrix` context directly (supported in continue-on-error):
61
+ ```yaml
62
+ strategy:
63
+ matrix:
64
+ experimental: [true, false]
65
+ steps:
66
+ - run: ./build.sh
67
+ continue-on-error: ${{ matrix.experimental }}
68
+ ```
69
+
70
+ **Option 3**: Wrap the step in a conditional run script that handles the error inline.
71
+ fix_code:
72
+ - language: yaml
73
+ label: "Use step output to control continue-on-error dynamically"
74
+ code: |
75
+ jobs:
76
+ test:
77
+ runs-on: ubuntu-latest
78
+ env:
79
+ EXPERIMENTAL_VERSIONS: "3.12 3.13"
80
+ steps:
81
+ - uses: actions/checkout@v4
82
+
83
+ # ❌ This FAILS: env context not available in continue-on-error
84
+ # - run: pytest
85
+ # continue-on-error: ${{ contains(env.EXPERIMENTAL_VERSIONS, matrix.python) }}
86
+
87
+ # ✅ Compute the flag first, then reference via steps context
88
+ - id: flags
89
+ run: |
90
+ if echo "$EXPERIMENTAL_VERSIONS" | grep -qw "${{ matrix.python }}"; then
91
+ echo "experimental=true" >> $GITHUB_OUTPUT
92
+ else
93
+ echo "experimental=false" >> $GITHUB_OUTPUT
94
+ fi
95
+ - run: pytest
96
+ continue-on-error: ${{ steps.flags.outputs.experimental == 'true' }}
97
+ - language: yaml
98
+ label: "Use matrix boolean directly (no env needed)"
99
+ code: |
100
+ jobs:
101
+ test:
102
+ strategy:
103
+ matrix:
104
+ include:
105
+ - python: "3.11"
106
+ experimental: false
107
+ - python: "3.12"
108
+ experimental: true
109
+ - python: "3.13"
110
+ experimental: true
111
+ runs-on: ubuntu-latest
112
+ steps:
113
+ - uses: actions/checkout@v4
114
+ - uses: actions/setup-python@v5
115
+ with:
116
+ python-version: ${{ matrix.python }}
117
+ - run: pytest
118
+ continue-on-error: ${{ matrix.experimental }} # ✅ matrix context IS supported
119
+ prevention:
120
+ - "Check the GitHub Actions context availability table before using any context in job-level attributes — not all contexts are available everywhere."
121
+ - "When you need dynamic boolean flags in `continue-on-error`, compute them in an earlier step and read via `steps.<id>.outputs`."
122
+ - "The `matrix` context is fully supported in `continue-on-error` — prefer encoding experimental flags in the matrix definition."
123
+ - "Run `actionlint` on your workflow to catch context-availability errors before pushing."
124
+ docs:
125
+ - url: "https://github.com/actions/runner/issues/1492"
126
+ label: "actions/runner#1492 — env context not available in continue-on-error (original report)"
127
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#context-availability"
128
+ label: "GitHub Docs — context availability table (where each context can be used)"
129
+ - url: "https://github.com/rhysd/actionlint"
130
+ label: "actionlint — static analyzer that catches context-availability errors"
@@ -0,0 +1,99 @@
1
+ id: yaml-syntax-021
2
+ title: "fromJSON() on empty or undefined step output throws Unexpected end of JSON input"
3
+ category: yaml-syntax
4
+ severity: error
5
+ tags:
6
+ - fromjson
7
+ - outputs
8
+ - expression
9
+ - json
10
+ - step-outputs
11
+ - dynamic-matrix
12
+ patterns:
13
+ - regex: "Unexpected end of JSON input"
14
+ flags: "i"
15
+ - regex: "Invalid JSON for parameter"
16
+ flags: "i"
17
+ - regex: "is not valid JSON"
18
+ flags: "i"
19
+ - regex: "fromJSON.*failed"
20
+ flags: "i"
21
+ error_messages:
22
+ - "Unexpected end of JSON input"
23
+ - "Error: Invalid JSON for parameter 'matrix': Unexpected end of JSON input"
24
+ - "Error: The expression result is not valid JSON"
25
+ root_cause: |
26
+ fromJSON() is a GitHub Actions expression function that parses a JSON string.
27
+ When the input string is empty ("") — which happens when a step output was
28
+ never set or when the step that set it was skipped due to an if: condition —
29
+ fromJSON() throws a fatal parse error: "Unexpected end of JSON input".
30
+
31
+ This is especially common in dynamic matrix strategies:
32
+
33
+ matrix:
34
+ include: ${{ fromJSON(steps.generate.outputs.matrix) }}
35
+
36
+ If the generate step was skipped (e.g., if: github.event_name == 'push'),
37
+ steps.generate.outputs.matrix evaluates to empty string, and fromJSON("")
38
+ crashes the entire job before any steps run.
39
+
40
+ The expression evaluator does NOT treat empty string as null or as valid
41
+ empty JSON — it passes the raw empty string directly to the JSON parser,
42
+ which rejects it immediately.
43
+ fix: |
44
+ Provide a fallback JSON value using the || operator so that fromJSON always
45
+ receives valid JSON even when the output is empty:
46
+
47
+ fromJSON(steps.generate.outputs.matrix || '[]')
48
+ fromJSON(steps.generate.outputs.config || '{}')
49
+
50
+ For dynamic matrix strategies, also ensure the generating step always
51
+ outputs valid JSON (at minimum '[]' or '{}') rather than leaving the
52
+ output unset when there is nothing to process.
53
+
54
+ Add an if: condition on downstream jobs to skip entirely when the matrix
55
+ output is empty or equals the empty-array sentinel.
56
+ fix_code:
57
+ - language: yaml
58
+ label: "Guard fromJSON with a valid JSON fallback using the || operator"
59
+ code: |
60
+ jobs:
61
+ compute-matrix:
62
+ runs-on: ubuntu-latest
63
+ outputs:
64
+ matrix: ${{ steps.gen.outputs.matrix }}
65
+ steps:
66
+ - id: gen
67
+ run: |
68
+ # Always emit valid JSON — even when there is nothing to build
69
+ TARGETS=$(cat targets.json 2>/dev/null || echo '[]')
70
+ echo "matrix=$TARGETS" >> $GITHUB_OUTPUT
71
+
72
+ build:
73
+ needs: compute-matrix
74
+ if: ${{ needs.compute-matrix.outputs.matrix != '[]' }}
75
+ strategy:
76
+ matrix:
77
+ # fromJSON never sees empty string — falls back to []
78
+ include: ${{ fromJSON(needs.compute-matrix.outputs.matrix || '[]') }}
79
+ runs-on: ubuntu-latest
80
+ steps:
81
+ - run: echo "Building ${{ matrix.target }}"
82
+ - language: yaml
83
+ label: "Inline fallback for dynamic matrix include"
84
+ code: |
85
+ strategy:
86
+ matrix:
87
+ include: ${{ fromJSON(needs.setup.outputs.matrix || '[{"env":"default"}]') }}
88
+ prevention:
89
+ - "Always provide a valid JSON fallback: fromJSON(steps.x.outputs.val || '{}')"
90
+ - "Ensure output-producing steps always write a value even if it is an empty array or object"
91
+ - "Add if: conditions to skip downstream jobs entirely when the matrix output is empty"
92
+ - "Validate fromJSON expressions with an empty-output test scenario in a draft workflow"
93
+ docs:
94
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#fromjson"
95
+ label: "GitHub Docs — fromJSON expression function"
96
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/passing-information-between-jobs"
97
+ label: "GitHub Docs — Passing information between jobs using outputs"
98
+ - url: "https://github.com/orgs/community/discussions/26671"
99
+ label: "GitHub Community #26671 — fromJSON fails when output is empty string"
@@ -0,0 +1,145 @@
1
+ id: yaml-syntax-020
2
+ title: "if: !cancelled() Triggers YAML Tag Parse Error — Must Quote or Use Expression Syntax"
3
+ category: yaml-syntax
4
+ severity: error
5
+ tags:
6
+ - if-condition
7
+ - cancelled
8
+ - yaml-tag
9
+ - expression-syntax
10
+ - negation
11
+ - bang-operator
12
+ - always-run
13
+ - parse-error
14
+ patterns:
15
+ - regex: "if:\\s*!(?:cancelled|failure|success|always)\\(\\)"
16
+ flags: "im"
17
+ - regex: "mapping values are not allowed here|did not find expected key|could not find expected"
18
+ flags: "i"
19
+ - regex: "yaml.*tag.*!\\w+|Unknown tag"
20
+ flags: "i"
21
+ error_messages:
22
+ - "mapping values are not allowed here"
23
+ - "could not find expected ':'"
24
+ - "did not find expected key"
25
+ - "Error: YAMLException: bad indentation of a mapping entry"
26
+ - "Invalid workflow file: You have an error in your yaml syntax"
27
+ root_cause: |
28
+ In YAML, `!` is a reserved indicator used to specify **explicit type tags**
29
+ (e.g., `!!str`, `!python/object`). When you write an unquoted `if:` value starting
30
+ with `!`, the YAML parser interprets it as a tag application — NOT as boolean negation.
31
+
32
+ For example:
33
+ ```
34
+ if: !cancelled() # YAML reads this as: apply tag "cancelled()" to null value
35
+ ```
36
+
37
+ Because `!cancelled()` is an invalid YAML tag application (no value follows the tag,
38
+ or the parentheses make it syntactically invalid as a tag identifier), this causes a
39
+ YAML parse error and the entire workflow file is rejected with messages like
40
+ "mapping values are not allowed here" or "bad indentation of a mapping entry".
41
+
42
+ This is a common mistake because developers familiar with programming languages expect
43
+ `!` to be a boolean NOT operator, which it is inside GitHub Actions expressions
44
+ (`${{ !cancelled() }}`), but NOT in bare YAML string values.
45
+
46
+ Affected operators:
47
+ - `if: !cancelled()` → YAML tag error
48
+ - `if: !failure()` → YAML tag error
49
+ - `if: !success()` → YAML tag error (rare, but same pattern)
50
+
51
+ The fix is either:
52
+ 1. Wrap in expression delimiters: `if: ${{ !cancelled() }}`
53
+ 2. Quote the value: `if: "!cancelled()"`
54
+ Both forms are valid GitHub Actions `if:` conditions.
55
+
56
+ Source: GitHub Community Discussion — common YAML parse error with if: conditions
57
+ Source: GitHub Actions documentation — Expressions syntax
58
+ fix: |
59
+ Choose one of two equivalent fixes:
60
+
61
+ Option 1 — Use expression syntax (recommended, more explicit):
62
+ `if: ${{ !cancelled() }}`
63
+
64
+ Option 2 — Quote the string (also valid):
65
+ `if: "!cancelled()"`
66
+
67
+ Both options tell the YAML parser to treat the value as a plain string / expression,
68
+ bypassing the tag interpretation.
69
+
70
+ Note: Inside `${{ }}` expression blocks, `!` IS the boolean NOT operator and works
71
+ as expected. Outside those blocks, bare YAML values starting with `!` are parsed as
72
+ YAML type tags.
73
+ fix_code:
74
+ - language: yaml
75
+ label: "Fix: use ${{ }} expression syntax for negation"
76
+ code: |
77
+ jobs:
78
+ cleanup:
79
+ runs-on: ubuntu-latest
80
+ steps:
81
+ # ❌ WRONG: YAML parse error — ! is a YAML tag indicator
82
+ - name: Run unless cancelled (broken)
83
+ if: !cancelled()
84
+ run: ./cleanup.sh
85
+
86
+ # ✅ CORRECT: wrap in expression delimiters
87
+ - name: Run unless cancelled (fixed)
88
+ if: ${{ !cancelled() }}
89
+ run: ./cleanup.sh
90
+
91
+ # ✅ ALSO CORRECT: quote the string
92
+ - name: Run unless cancelled (also fixed)
93
+ if: "!cancelled()"
94
+ run: ./cleanup.sh
95
+
96
+ - language: yaml
97
+ label: "Common patterns: negating status check functions"
98
+ code: |
99
+ jobs:
100
+ notify:
101
+ runs-on: ubuntu-latest
102
+ steps:
103
+ # Run step if previous steps did NOT fail
104
+ - name: Send success notification
105
+ if: ${{ !failure() }}
106
+ run: ./notify-success.sh
107
+
108
+ # Run if job was NOT cancelled (catch failures too)
109
+ - name: Always-ish cleanup (skips on cancel)
110
+ if: ${{ !cancelled() }}
111
+ run: ./cleanup.sh
112
+
113
+ # Job-level condition: run this job if parent was not skipped or failed
114
+ # (equivalent to always() but excluding explicitly-failed states)
115
+
116
+ - language: yaml
117
+ label: "Using always() instead of !cancelled() when appropriate"
118
+ code: |
119
+ jobs:
120
+ report:
121
+ runs-on: ubuntu-latest
122
+ # always() runs regardless of success, failure, OR cancellation
123
+ # !cancelled() runs on success OR failure, but NOT cancellation
124
+ steps:
125
+ - name: Report always (even if cancelled)
126
+ if: always()
127
+ run: ./post-report.sh
128
+
129
+ - name: Report unless cancelled
130
+ if: ${{ !cancelled() }}
131
+ run: ./post-report-unless-cancelled.sh
132
+
133
+ prevention:
134
+ - "Always wrap boolean expressions using `!`, `&&`, or `||` inside `${{ }}` delimiters in `if:` conditions."
135
+ - "Quote any `if:` value that starts with `!` if you prefer not to use expression syntax."
136
+ - "Use `always()` when you want to run regardless of success, failure, AND cancellation."
137
+ - "Use `!cancelled()` (inside `${{ }}`) when you want to run on success or failure, but not cancellation."
138
+ - "Lint workflows with `actionlint` locally — it catches YAML tag parse errors before push."
139
+ docs:
140
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#operators"
141
+ label: "GitHub Docs: Expressions — operators (! boolean NOT)"
142
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#status-check-functions"
143
+ label: "GitHub Docs: Expressions — status check functions (cancelled, failure, success)"
144
+ - url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsif"
145
+ label: "GitHub Docs: Workflow syntax — steps.if"