@htekdev/actions-debugger 1.0.1 → 1.0.2

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,101 @@
1
+ id: known-unsolved-007
2
+ title: "Workflow Rerun Limit: 50 Reruns Maximum Per Workflow Run"
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - rerun
7
+ - retry
8
+ - automation
9
+ - limits
10
+ - check-suite
11
+ patterns:
12
+ - regex: "exceeded.*maximum.*rerun.*limit"
13
+ flags: "i"
14
+ - regex: "rerun limit.*50"
15
+ flags: "i"
16
+ - regex: "This workflow has been rerun too many times"
17
+ flags: "i"
18
+ - regex: "maximum rerun limit.*exceeded"
19
+ flags: "i"
20
+ error_messages:
21
+ - "This workflow has exceeded the maximum rerun limit of 50."
22
+ - "You have exceeded the maximum rerun limit for this workflow run."
23
+ - "Failed check suite: exceeded maximum rerun limit."
24
+ root_cause: |
25
+ In April 2026 GitHub introduced a hard cap of 50 reruns per workflow run (combining
26
+ full re-runs and partial job re-runs). When the limit is hit, any subsequent rerun
27
+ attempt results in a failed check suite with an annotation — no partial or full rerun
28
+ is permitted on that run ever again.
29
+
30
+ The limit was introduced because some automation scripts were issuing hundreds of retry
31
+ attempts on a single workflow run, adding significant load to GitHub's infrastructure.
32
+
33
+ Common triggers of this limit:
34
+ - Automated retry bots (e.g. flaky-test detection scripts) that loop on `gh run rerun`
35
+ - CI platforms that aggressively requeue failed runs
36
+ - Scheduled maintenance pipelines that retry indefinitely on runner-level transient failures
37
+ - GitHub Apps polling and re-triggering stale check suites on long-lived PRs
38
+ fix: |
39
+ There is no way to raise the 50-rerun cap — it is a hard platform limit with no bypass.
40
+
41
+ Recommended approaches:
42
+ 1. **Fix the root cause instead of retrying** — identify and address flaky tests, network
43
+ timeouts, or runner instability rather than masking them with reruns.
44
+ 2. **Create a new workflow run instead of rerunning** — close and reopen the PR, push a
45
+ no-op commit, or trigger `workflow_dispatch` to start a fresh run with its own 50-run
46
+ budget.
47
+ 3. **Implement retry logic inside the step** — use a step-level retry loop (`for i in 1 2 3`)
48
+ rather than whole-workflow reruns so flakiness is contained within a single run.
49
+ 4. **Limit automation rerun budgets** — if you operate a retry bot, add a counter check
50
+ (`gh run view --json runAttempt`) and stop retrying once `runAttempt >= 40` to leave
51
+ headroom before the limit hits.
52
+ fix_code:
53
+ - language: yaml
54
+ label: "Step-level retry instead of whole-workflow rerun"
55
+ code: |
56
+ steps:
57
+ - name: Run flaky integration test (with built-in retry)
58
+ shell: bash
59
+ run: |
60
+ for attempt in 1 2 3; do
61
+ echo "Attempt $attempt of 3"
62
+ if npm run test:integration; then
63
+ echo "Tests passed on attempt $attempt"
64
+ exit 0
65
+ fi
66
+ echo "Attempt $attempt failed, retrying..."
67
+ sleep 10
68
+ done
69
+ echo "All 3 attempts failed"
70
+ exit 1
71
+ - language: yaml
72
+ label: "Check rerun count before auto-retrying in a workflow automation"
73
+ code: |
74
+ steps:
75
+ - name: Guard against rerun limit
76
+ shell: bash
77
+ run: |
78
+ RUN_ATTEMPT="${{ github.run_attempt }}"
79
+ if [ "$RUN_ATTEMPT" -ge 40 ]; then
80
+ echo "::error::Run attempt $RUN_ATTEMPT is near the 50-rerun limit. Stopping auto-retry."
81
+ exit 1
82
+ fi
83
+ echo "Run attempt $RUN_ATTEMPT — within safe retry budget"
84
+ - language: yaml
85
+ label: "Trigger a fresh run instead of rerunning (workflow_dispatch)"
86
+ code: |
87
+ # Instead of gh run rerun <run-id>, dispatch a fresh run:
88
+ # gh workflow run my-workflow.yml --ref main
89
+ # This creates a new run ID with its own 50-rerun budget.
90
+ prevention:
91
+ - "Build retry logic inside steps using shell loops rather than whole-workflow reruns."
92
+ - "Monitor `github.run_attempt` context value; alert or stop automation at 40+ attempts."
93
+ - "Fix flaky tests rather than relying on reruns as the primary reliability mechanism."
94
+ - "If a run regularly hits >10 reruns, treat it as a reliability incident, not a normal CI pattern."
95
+ docs:
96
+ - url: "https://github.blog/changelog/2026-04-10-actions-workflows-are-limited-to-50-reruns/"
97
+ label: "GitHub Changelog: Actions workflows are limited to 50 reruns"
98
+ - url: "https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/re-running-workflows-and-jobs"
99
+ label: "Re-running workflows and jobs"
100
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions"
101
+ label: "Workflow commands for GitHub Actions"
@@ -0,0 +1,130 @@
1
+ id: permissions-auth-008
2
+ title: "GCP OIDC Workload Identity: Pool Path vs Provider Path / Project ID vs Number"
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - oidc
7
+ - gcp
8
+ - google-cloud
9
+ - workload-identity
10
+ - auth
11
+ patterns:
12
+ - regex: "Invalid value for [\"']?audience[\"']?"
13
+ flags: "i"
14
+ - regex: "Error code invalid_request.*Invalid value for.*audience"
15
+ flags: "i"
16
+ - regex: "workloadIdentityPools.*not.*providers"
17
+ flags: "i"
18
+ - regex: "project.*number.*not.*project.*id"
19
+ flags: "i"
20
+ - regex: "OAuthError.*Invalid value for.*audience"
21
+ flags: "i"
22
+ - regex: "Error parsing credentials.*workload_identity_provider"
23
+ flags: "i"
24
+ error_messages:
25
+ - "ERROR: gcloud crashed (OAuthError): Error code invalid_request: Invalid value for \"audience\"."
26
+ - "Error: google-github-actions/auth failed with: Error code invalid_request: Invalid value for \"audience\"."
27
+ - "The workload_identity_provider must be the full provider resource name, not the pool resource name."
28
+ root_cause: |
29
+ `google-github-actions/auth` OIDC authentication fails with "Invalid value for audience"
30
+ when either of two common misconfiguration patterns is present:
31
+
32
+ **Mistake 1 — Pool path instead of Provider path**
33
+ The `workload_identity_provider` input requires the full *Provider* resource name:
34
+ `projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID`
35
+
36
+ Many developers mistakenly supply only the *Pool* path (omitting `/providers/PROVIDER_ID`):
37
+ `projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID`
38
+
39
+ GCP's STS token endpoint rejects the audience because it expects the provider URI, not the pool URI.
40
+
41
+ **Mistake 2 — Project ID (string) instead of Project Number (integer)**
42
+ The path requires the numeric GCP project number (e.g. `123456789012`), NOT the human-readable
43
+ project ID (e.g. `my-gcp-project`). Workload Identity Federation does not accept project IDs.
44
+ Using a project ID causes the STS endpoint to return "Invalid value for audience" because the
45
+ resource path does not resolve to a valid identity provider.
46
+
47
+ **Mistake 3 — Missing `id-token: write` permission**
48
+ If `permissions.id-token` is not explicitly set to `write` at the job or workflow level,
49
+ GitHub will not mint an OIDC token and the auth step fails with a permissions error rather
50
+ than a token exchange error.
51
+ fix: |
52
+ Verify the `workload_identity_provider` value against the exact format required:
53
+ `projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL/providers/PROVIDER`
54
+
55
+ Retrieve the correct value:
56
+ gcloud iam workload-identity-pools providers describe PROVIDER_ID \
57
+ --project=PROJECT_ID \
58
+ --location=global \
59
+ --workload-identity-pool=POOL_ID \
60
+ --format='value(name)'
61
+
62
+ Ensure `id-token: write` is set in the job permissions block.
63
+
64
+ Wait at least 5 minutes after changes to Workload Identity Pool / Provider / IAM bindings
65
+ before testing — these resources are eventually consistent.
66
+ fix_code:
67
+ - language: yaml
68
+ label: "Correct workload_identity_provider format (provider path + project number)"
69
+ code: |
70
+ jobs:
71
+ deploy:
72
+ runs-on: ubuntu-latest
73
+ permissions:
74
+ id-token: write # REQUIRED — grants GitHub the right to mint an OIDC token
75
+ contents: read
76
+
77
+ steps:
78
+ - uses: actions/checkout@v4
79
+
80
+ - id: auth
81
+ uses: google-github-actions/auth@v2
82
+ with:
83
+ # CORRECT: full provider path with numeric project number
84
+ workload_identity_provider: >-
85
+ projects/123456789012/locations/global/workloadIdentityPools/my-pool/providers/my-provider
86
+ service_account: my-service-account@my-project.iam.gserviceaccount.com
87
+ - language: yaml
88
+ label: "Common mistake — pool path (missing /providers/...) causes audience error"
89
+ code: |
90
+ # WRONG: pool path only — gcloud crashes with "Invalid value for audience"
91
+ # workload_identity_provider: >-
92
+ # projects/123456789012/locations/global/workloadIdentityPools/my-pool
93
+
94
+ # ALSO WRONG: project ID string instead of project number
95
+ # workload_identity_provider: >-
96
+ # projects/my-gcp-project/locations/global/workloadIdentityPools/my-pool/providers/my-provider
97
+
98
+ # CORRECT:
99
+ # workload_identity_provider: >-
100
+ # projects/123456789012/locations/global/workloadIdentityPools/my-pool/providers/my-provider
101
+ - language: yaml
102
+ label: "Store provider path in a repository variable to avoid typos"
103
+ code: |
104
+ jobs:
105
+ deploy:
106
+ runs-on: ubuntu-latest
107
+ permissions:
108
+ id-token: write
109
+ contents: read
110
+ steps:
111
+ - uses: google-github-actions/auth@v2
112
+ with:
113
+ # Store full provider path as a repository variable to avoid typos
114
+ workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}
115
+ service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
116
+ prevention:
117
+ - "Always use `gcloud iam workload-identity-pools providers describe` to copy the exact provider path."
118
+ - "Store the full provider path in a GitHub Actions variable (`vars.GCP_WIF_PROVIDER`) to prevent manual typos."
119
+ - "Use numeric project number — retrieve it with `gcloud projects describe PROJECT_ID --format='value(projectNumber)'`."
120
+ - "After changing Workload Identity Pool config, wait 5 minutes before testing (eventual consistency)."
121
+ - "Confirm `id-token: write` permission is present at job (not just workflow) level."
122
+ docs:
123
+ - url: "https://github.com/google-github-actions/auth#setup"
124
+ label: "google-github-actions/auth: Setup and configuration"
125
+ - url: "https://github.com/google-github-actions/auth/blob/main/docs/TROUBLESHOOTING.md"
126
+ label: "google-github-actions/auth: Troubleshooting guide"
127
+ - url: "https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform"
128
+ label: "Configuring OIDC in Google Cloud Platform"
129
+ - url: "https://cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines"
130
+ label: "GCP: Workload Identity Federation with deployment pipelines"
@@ -0,0 +1,118 @@
1
+ id: runner-environment-016
2
+ title: "Node 20 → Node 24 Forced Migration Breaks Actions and macOS 13 Runners"
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - node
7
+ - node24
8
+ - deprecation
9
+ - macos
10
+ - arm32
11
+ - runtime-migration
12
+ patterns:
13
+ - regex: "Node\\.?20 actions are deprecated"
14
+ flags: "i"
15
+ - regex: "Please update the following actions to use Node\\.?24"
16
+ flags: "i"
17
+ - regex: "node20 is deprecated"
18
+ flags: "i"
19
+ - regex: "macOS 13.*not supported.*Node 24"
20
+ flags: "i"
21
+ - regex: "ARM32.*no longer supported"
22
+ flags: "i"
23
+ error_messages:
24
+ - "Node.js 20 actions are deprecated. Please update the following actions to use Node.js 24."
25
+ - "node20 is deprecated and will be disabled in a future runner release."
26
+ - "Error: This action requires Node.js 24 or higher. Current version: 20."
27
+ root_cause: |
28
+ GitHub announced deprecation of Node 20 on Actions runners on September 19, 2025
29
+ (editor updated May 19, 2026: migration date confirmed June 16, 2026). Starting
30
+ June 16, 2026, all GitHub-hosted runners default to Node 24.
31
+
32
+ Three distinct breakage scenarios exist:
33
+
34
+ 1. **Marketplace actions using `runs.using: 'node20'`** — any third-party or custom
35
+ action that declares `runs.using: node20` in its `action.yml` will emit deprecation
36
+ warnings and eventually fail when GitHub removes Node 20 from runners later in 2026.
37
+
38
+ 2. **macOS 13 (and older) runners are incompatible with Node 24** — Node 24 dropped
39
+ support for macOS 13.4 and lower. Workflows specifying `runs-on: macos-13` (or older
40
+ images) fail at the runner startup phase or produce unexpected errors from the
41
+ Node-based runner bootstrapper.
42
+
43
+ 3. **ARM32 self-hosted runners** — Node 24 has no official ARM32 support. Self-hosted
44
+ runners on ARM32 hardware silently lose the ability to execute Node-based actions
45
+ after the Node 20 removal milestone.
46
+ fix: |
47
+ **For action authors:** Update `action.yml` to declare `runs.using: 'node24'` and
48
+ test locally with Node 24. Publish a new release so downstream consumers pick it up.
49
+
50
+ **For workflow authors:**
51
+ - Upgrade all `uses:` pins to the latest major version that ships with Node 24 support
52
+ (e.g. `actions/checkout@v4 → @v4` already Node-24-ready; `actions/setup-node@v4`
53
+ already Node-24-ready).
54
+ - Replace `runs-on: macos-13` with `runs-on: macos-latest` or `macos-14+`.
55
+ - For ARM32 self-hosted runners: migrate to ARM64 hardware or use container-based
56
+ execution that bundles its own Node runtime.
57
+
58
+ **Temporary escape hatch (not for production long-term):**
59
+ Set `ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true` in the workflow `env` block to
60
+ continue using Node 20 until GitHub removes it from runners later in 2026.
61
+ fix_code:
62
+ - language: yaml
63
+ label: "Upgrade pinned action versions to Node 24-compatible releases"
64
+ code: |
65
+ steps:
66
+ # Pin to latest major — all official actions already ship Node 24 builds
67
+ - uses: actions/checkout@v4
68
+ - uses: actions/setup-node@v4
69
+ with:
70
+ node-version: '20'
71
+ - uses: actions/upload-artifact@v4
72
+ with:
73
+ name: dist
74
+ path: dist/
75
+ - language: yaml
76
+ label: "Migrate macOS runner from macos-13 to macos-latest"
77
+ code: |
78
+ jobs:
79
+ build:
80
+ # macos-13 is incompatible with Node 24 — use macos-latest (14+)
81
+ runs-on: macos-latest
82
+ steps:
83
+ - uses: actions/checkout@v4
84
+ - language: yaml
85
+ label: "Temporary escape hatch — keep Node 20 until explicit removal"
86
+ code: |
87
+ jobs:
88
+ build:
89
+ runs-on: ubuntu-latest
90
+ env:
91
+ # WARNING: temporary only — Node 20 will be fully removed later in 2026
92
+ ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: 'true'
93
+ steps:
94
+ - uses: actions/checkout@v4
95
+ - language: yaml
96
+ label: "Test Node 24 compatibility before the mandatory cutover"
97
+ code: |
98
+ jobs:
99
+ build:
100
+ runs-on: ubuntu-latest
101
+ env:
102
+ # Force Node 24 to test compatibility ahead of the June 16 cutover
103
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
104
+ steps:
105
+ - uses: actions/checkout@v4
106
+ prevention:
107
+ - "Subscribe to GitHub changelog https://github.blog/changelog/label/actions/ for deprecation notices."
108
+ - "Run `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true` in CI to catch Node 24 incompatibilities early."
109
+ - "Avoid pinning to old major versions of `actions/*` — use floating major tags (e.g. @v4)."
110
+ - "Audit custom/internal actions for `runs.using: node20` declarations before the June 16, 2026 migration date."
111
+ - "Migrate macOS CI to macos-14 or macos-latest to ensure Node 24 compatibility."
112
+ docs:
113
+ - url: "https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/"
114
+ label: "GitHub Changelog: Deprecation of Node 20 on GitHub Actions runners"
115
+ - url: "https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs-for-javascript-actions"
116
+ label: "Metadata syntax: runs.using for JavaScript actions"
117
+ - url: "https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources"
118
+ label: "Supported runners and hardware resources"
@@ -0,0 +1,120 @@
1
+ id: silent-failures-008
2
+ title: "Sparse Checkout Persists to Subsequent Checkout Steps (Sticky Cone Mode)"
3
+ category: silent-failures
4
+ severity: silent-failure
5
+ tags:
6
+ - checkout
7
+ - sparse-checkout
8
+ - cone-mode
9
+ - composite-actions
10
+ - file-missing
11
+ patterns:
12
+ - regex: "sparse.*checkout.*persist"
13
+ flags: "i"
14
+ - regex: "core\\.sparseCheckout.*true"
15
+ flags: "i"
16
+ - regex: "sparse-checkout.*not disabled"
17
+ flags: "i"
18
+ error_messages:
19
+ - "Run actions/checkout@v4"
20
+ - "Expected full checkout but only sparse tree present"
21
+ root_cause: |
22
+ When `actions/checkout` is called with `sparse-checkout:` options, it sets the git
23
+ repository's `core.sparseCheckout = true` config. A subsequent call to `actions/checkout`
24
+ in the same job — even WITHOUT specifying sparse-checkout — re-uses the existing git
25
+ repository and inherits the sticky `core.sparseCheckout = true` setting.
26
+
27
+ Result: the second checkout appears to succeed (no error), but the working directory
28
+ still contains only the sparse subset from the first checkout. Files outside the
29
+ original sparse pattern are silently absent.
30
+
31
+ This most commonly occurs in two scenarios:
32
+ 1. A workflow calls `actions/checkout` with sparse-checkout for a fast initial clone,
33
+ then calls `actions/checkout` again (different ref, different path spec) expecting
34
+ a full checkout.
35
+ 2. A workflow uses sparse-checkout, then calls a composite action that internally runs
36
+ its own `actions/checkout`. The composite action's checkout inherits the sparse
37
+ setting from the parent workflow's checkout.
38
+
39
+ Root cause: a bug in `actions/checkout` — the `disableSparseCheckout()` method does not
40
+ explicitly set `core.sparseCheckout false` when `sparse-checkout` input is absent. A
41
+ fix PR (#2034) exists in the repo but has not been merged as of 2026.
42
+ fix: |
43
+ **Option 1 (recommended): Explicitly reset sparse-checkout between checkouts**
44
+ Add a `git sparse-checkout disable` step between the sparse and full checkouts. This
45
+ clears the sticky `core.sparseCheckout` flag and ensures subsequent checkouts are full.
46
+
47
+ **Option 2: Use separate `path:` directories**
48
+ Checkout into a unique subdirectory with the `path:` input to prevent git config
49
+ sharing. Each `path:` gets its own `.git` config.
50
+
51
+ **Option 3: Use `sparse-checkout-cone-mode: false` carefully**
52
+ When in non-cone mode, review that patterns don't accidentally match more or fewer
53
+ files than expected. Non-cone mode with incorrect patterns is its own source of
54
+ silent failures.
55
+ fix_code:
56
+ - language: yaml
57
+ label: "Reset sparse-checkout before a subsequent full checkout"
58
+ code: |
59
+ steps:
60
+ # First checkout: sparse (fast clone for config files only)
61
+ - uses: actions/checkout@v4
62
+ with:
63
+ sparse-checkout: |
64
+ .github
65
+ config/
66
+
67
+ - name: Reset sparse-checkout so next checkout is full
68
+ shell: bash
69
+ run: git sparse-checkout disable
70
+
71
+ # Second checkout (in composite action or next step): now gets full tree
72
+ - uses: actions/checkout@v4
73
+ with:
74
+ ref: ${{ github.sha }}
75
+ - language: yaml
76
+ label: "Use separate path: directories to avoid shared git config"
77
+ code: |
78
+ steps:
79
+ # Sparse checkout into 'config-only/' subdirectory
80
+ - uses: actions/checkout@v4
81
+ with:
82
+ path: config-only
83
+ sparse-checkout: |
84
+ config/
85
+
86
+ # Full checkout into 'full-repo/' — completely separate git repo config
87
+ - uses: actions/checkout@v4
88
+ with:
89
+ path: full-repo
90
+ - language: yaml
91
+ label: "Composite action defensive reset (add to composite action's beginning)"
92
+ code: |
93
+ # In your composite action's action.yml — reset sparse before own checkout
94
+ runs:
95
+ using: composite
96
+ steps:
97
+ - name: Reset any inherited sparse-checkout
98
+ shell: bash
99
+ run: |
100
+ if git rev-parse --git-dir > /dev/null 2>&1; then
101
+ git sparse-checkout disable 2>/dev/null || true
102
+ fi
103
+
104
+ - uses: actions/checkout@v4
105
+ with:
106
+ ref: ${{ inputs.ref }}
107
+ prevention:
108
+ - "Never assume a subsequent `actions/checkout` step gets a full tree if any earlier step used sparse-checkout."
109
+ - "Add `git sparse-checkout disable` as an explicit step between sparse and full checkouts in the same job."
110
+ - "When writing composite actions that call `actions/checkout`, add a defensive `git sparse-checkout disable` before your checkout step."
111
+ - "Use the `path:` input to isolate checkouts that need different content into separate directories."
112
+ docs:
113
+ - url: "https://github.com/actions/checkout/issues/1498"
114
+ label: "actions/checkout#1498: Sparse checkout persists in composite action"
115
+ - url: "https://github.com/actions/checkout/pull/2034"
116
+ label: "actions/checkout#2034: Fix — disable sparse-checkout on subsequent checkout (open PR)"
117
+ - url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses"
118
+ label: "Workflow syntax: steps.uses"
119
+ - url: "https://github.com/actions/checkout#usage"
120
+ label: "actions/checkout: Usage and sparse-checkout options"
@@ -0,0 +1,140 @@
1
+ id: yaml-syntax-013
2
+ title: "Reusable Workflow Output Missing on.workflow_call.outputs Declaration"
3
+ category: yaml-syntax
4
+ severity: silent-failure
5
+ tags:
6
+ - reusable-workflow
7
+ - workflow-call
8
+ - outputs
9
+ - silent-failure
10
+ - job-outputs
11
+ patterns:
12
+ - regex: "needs\\.[a-zA-Z0-9_-]+\\.outputs\\.[a-zA-Z0-9_-]+"
13
+ flags: "i"
14
+ - regex: "on\\.workflow_call\\.outputs.*not defined"
15
+ flags: "i"
16
+ - regex: "output.*reusable.*workflow.*empty"
17
+ flags: "i"
18
+ error_messages:
19
+ - "The output variable was not found in the called workflow's on.workflow_call.outputs map."
20
+ - "Output 'version' not found in called workflow."
21
+ root_cause: |
22
+ A called (reusable) workflow exposes job-level outputs but forgets to declare them at
23
+ the `workflow_call` trigger level. As a result the caller workflow's
24
+ `needs.<called-job>.outputs.<name>` expression evaluates to an empty string with NO
25
+ error message — a silent failure.
26
+
27
+ GitHub Actions requires a two-layer output declaration for reusable workflows:
28
+ 1. The job inside the called workflow declares step outputs via `outputs:` on the job.
29
+ 2. The called workflow's `on.workflow_call.outputs:` section explicitly maps workflow-
30
+ level output names to job-level output expressions.
31
+
32
+ If layer 2 is missing, the caller never receives the value even though layer 1 exists.
33
+ Because the expression resolves to an empty string (not an error), downstream steps may
34
+ silently receive wrong values — version tags become empty strings, Docker image names
35
+ become malformed, deploy environment names become blank.
36
+
37
+ A second variant occurs when accessing outputs from a called workflow that uses a matrix:
38
+ matrix job outputs cannot be aggregated automatically at the workflow level, so outputs
39
+ from individual matrix legs are inaccessible to the caller.
40
+ fix: |
41
+ Add the `on.workflow_call.outputs:` section to the called workflow, mapping each
42
+ desired output name to the corresponding job output expression.
43
+
44
+ For matrix jobs: aggregate outputs into a single job (e.g. using `toJSON`) or use a
45
+ final non-matrix aggregator job inside the called workflow that reads matrix outputs
46
+ and re-exposes them as a single value.
47
+ fix_code:
48
+ - language: yaml
49
+ label: "Called workflow — correct two-layer output declaration"
50
+ code: |
51
+ # .github/workflows/build-and-version.yml (CALLED workflow)
52
+ on:
53
+ workflow_call:
54
+ # Layer 2: workflow-level outputs (REQUIRED for caller to receive values)
55
+ outputs:
56
+ version:
57
+ description: "The computed version string"
58
+ value: ${{ jobs.build.outputs.version }}
59
+ artifact-name:
60
+ description: "Name of the uploaded artifact"
61
+ value: ${{ jobs.build.outputs.artifact-name }}
62
+
63
+ jobs:
64
+ build:
65
+ runs-on: ubuntu-latest
66
+ # Layer 1: job-level outputs
67
+ outputs:
68
+ version: ${{ steps.compute-version.outputs.version }}
69
+ artifact-name: ${{ steps.upload.outputs.artifact-name }}
70
+ steps:
71
+ - id: compute-version
72
+ run: echo "version=1.2.3-${{ github.sha }}" >> $GITHUB_OUTPUT
73
+
74
+ - id: upload
75
+ uses: actions/upload-artifact@v4
76
+ with:
77
+ name: build-output
78
+ path: dist/
79
+ - language: yaml
80
+ label: "Caller workflow — consuming outputs from called workflow"
81
+ code: |
82
+ # .github/workflows/deploy.yml (CALLER workflow)
83
+ jobs:
84
+ build:
85
+ uses: ./.github/workflows/build-and-version.yml
86
+ secrets: inherit
87
+
88
+ deploy:
89
+ needs: build
90
+ runs-on: ubuntu-latest
91
+ steps:
92
+ - name: Deploy version
93
+ # This only works if the called workflow has on.workflow_call.outputs declared
94
+ run: |
95
+ echo "Deploying version: ${{ needs.build.outputs.version }}"
96
+ echo "Using artifact: ${{ needs.build.outputs.artifact-name }}"
97
+ - language: yaml
98
+ label: "Aggregate matrix outputs in a final job for caller consumption"
99
+ code: |
100
+ # Called workflow with matrix — aggregate results before exposing as outputs
101
+ on:
102
+ workflow_call:
103
+ outputs:
104
+ all-results:
105
+ value: ${{ jobs.aggregate.outputs.results }}
106
+
107
+ jobs:
108
+ test:
109
+ runs-on: ubuntu-latest
110
+ strategy:
111
+ matrix:
112
+ suite: [unit, integration, e2e]
113
+ outputs:
114
+ result-${{ matrix.suite }}: ${{ steps.run.outputs.result }}
115
+ steps:
116
+ - id: run
117
+ run: echo "result=passed" >> $GITHUB_OUTPUT
118
+
119
+ # Aggregator job: collects matrix outputs and re-exposes as single value
120
+ aggregate:
121
+ needs: test
122
+ runs-on: ubuntu-latest
123
+ outputs:
124
+ results: ${{ steps.collect.outputs.results }}
125
+ steps:
126
+ - id: collect
127
+ run: |
128
+ echo "results=${{ toJSON(needs.test.outputs) }}" >> $GITHUB_OUTPUT
129
+ prevention:
130
+ - "Always declare `on.workflow_call.outputs:` in a reusable workflow if any caller needs its outputs."
131
+ - "Test output propagation by printing `${{ needs.<job>.outputs.<name> }}` in a debug step on the caller side."
132
+ - "Treat empty string outputs from called workflows as a sign of missing `on.workflow_call.outputs:` mapping."
133
+ - "Matrix jobs inside called workflows require an aggregator job — matrix outputs cannot be directly mapped."
134
+ docs:
135
+ - url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows#using-outputs-from-a-reusable-workflow"
136
+ label: "Reusable workflows: using outputs"
137
+ - url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onworkflow_calloutputs"
138
+ label: "Workflow syntax: on.workflow_call.outputs"
139
+ - url: "https://stackoverflow.com/questions/73702333/github-actions-reuse-outputs-from-other-reusable-workflows"
140
+ label: "Stack Overflow: Reuse outputs from reusable workflows (82 upvotes)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@htekdev/actions-debugger",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "65+ real GitHub Actions errors, queryable by agents. MCP server + Copilot skills + error database.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",