Summary
dispatch-workflow safe outputs can fail in workflow_call relay workflows because the compiler-injected target-ref is sourced from needs.activation.outputs.target_ref, and resolve_host_repo.cjs currently sets that output to JOB_WORKFLOW_SHA.
The GitHub workflow dispatch API expects ref to be a branch or tag ref. When dispatch_workflow.cjs forwards the SHA as ref, dispatch fails with:
Failed to dispatch workflow "<workflow-name>": No ref found for: <workflow commit sha> - https://docs.github.com/rest/actions/workflows#create-a-workflow-dispatch-event
This appears to have regressed in 5a61a7c6fe9dcb35da3802a359612380147d0181 / #25697 (feat: use job.workflow_* context for host repo resolution), which is included starting in v0.68.2. Pinning the setup action back to v0.68.1 makes the same workflow dispatch successfully.
Impact
This breaks workflows that:
- use
workflow_call relay behavior,
- configure
safe-outputs.dispatch-workflow, and
- rely on the compiler-injected
target-ref for dispatching a same-repo worker workflow from the safe outputs job.
The agent phase succeeds and emits dispatch_workflow items, but the safe_outputs job fails to dispatch each worker because ref is a SHA rather than a branch/tag ref.
Agent analysis
I analyzed the local source and compared the known-good v0.68.1 setup action against v0.71.1 and main.
Relevant runtime path
-
pkg/workflow/compiler_safe_outputs_config.go
- For
workflow_call workflows with safe-outputs.dispatch-workflow, the compiler injects:
target-repo: ${{ needs.activation.outputs.target_repo }}
target-ref: ${{ needs.activation.outputs.target_ref }}
-
pkg/workflow/compiler_activation_job.go
- The activation job exposes:
target_ref: ${{ steps.resolve-host-repo.outputs.target_ref }}
- It passes both
JOB_WORKFLOW_SHA: ${{ job.workflow_sha }} and JOB_WORKFLOW_REF: ${{ job.workflow_ref }} to resolve_host_repo.cjs.
-
actions/setup/js/resolve_host_repo.cjs
- On
main and v0.71.1, this currently does:
const targetRepo = process.env.JOB_WORKFLOW_REPOSITORY || "";
const targetRef = process.env.JOB_WORKFLOW_SHA || "";
actions/setup/js/dispatch_workflow.cjs
- If
config["target-ref"] is present, it unconditionally dispatches using that value:
if (config["target-ref"]) {
ref = config["target-ref"];
}
await githubClient.rest.actions.createWorkflowDispatch({
owner: repo.owner,
repo: repo.repo,
workflow_id: workflowFile,
ref: ref,
inputs: inputs,
return_run_details: true,
});
Because target-ref is now the workflow SHA, createWorkflowDispatch receives a SHA for ref and fails with No ref found for: <sha>.
Regression point
git log -S "JOB_WORKFLOW_SHA" -- actions/setup/js/resolve_host_repo.cjs identifies:
5a61a7c6fe feat: use job.workflow_* context for host repo resolution (#25697)
git tag --contains 5a61a7c6fe shows the change is present in v0.68.2 and later. v0.68.1 does not contain it.
The commit replaced the older resolver, which parsed the branch/tag from GITHUB_WORKFLOW_REF, with a simplified resolver that always emits JOB_WORKFLOW_SHA as target_ref. It also deleted actions/setup/js/resolve_host_repo.test.cjs, which previously covered this resolver behavior.
Why using SHA is unsafe for this output
Using a SHA is useful for actions/checkout ref: because checkout can pin to a commit. However, the same target_ref output is also used by dispatch-workflow safe outputs as the REST API ref field.
The API contract for createWorkflowDispatch requires a branch or tag ref; it does not accept an arbitrary workflow commit SHA in this scenario. So one output cannot safely serve both:
- activation checkout pinning, where SHA is valid/desirable, and
- workflow dispatch API
ref, where branch/tag is required.
Expected behavior
dispatch-workflow safe outputs in a workflow_call relay should dispatch the target workflow using a branch or tag ref that exists in the target repository, typically derived from job.workflow_ref.
For example, if:
JOB_WORKFLOW_REF=owner/repo/.github/workflows/orchestrator.lock.yml@refs/heads/main
JOB_WORKFLOW_SHA=<commit sha>
then the dispatch ref should be refs/heads/main (or main, if normalized), not <commit sha>.
Activation checkout can still pin to JOB_WORKFLOW_SHA if desired, but the value forwarded to dispatch_workflow.cjs must be dispatch-compatible.
Actual behavior
resolve_host_repo.cjs emits:
The safe outputs handler then calls:
createWorkflowDispatch({ ref: "<commit sha>", ... })
and the API returns:
No ref found for: <commit sha>
Reproduction
Minimal workflow shape:
---
on:
workflow_dispatch:
workflow_call:
safe-outputs:
dispatch-workflow:
workflows: [repo-worker]
max: 1
---
# Orchestrator
Emit a `dispatch_workflow` safe output for `repo-worker`.
repo-worker.md / repo-worker.lock.yml should define workflow_dispatch.
Run the orchestrator on a non-default or default branch using a setup action version containing #25697 (for example v0.71.1). The safe outputs handler receives a compiler-injected target-ref from needs.activation.outputs.target_ref, which resolves to job.workflow_sha, and dispatch fails with No ref found for: <sha>.
As a workaround, compiling the same workflow with:
gh aw compile --action-tag v0.68.1
or otherwise pinning the setup action to v0.68.1 makes dispatch succeed because the older resolver emits the branch/tag component from GITHUB_WORKFLOW_REF.
Proposed implementation plan
Please implement the following changes with a coding agent.
-
Split checkout ref from dispatch ref
- In
actions/setup/js/resolve_host_repo.cjs, produce separate outputs for the two different API contracts:
target_checkout_ref (or similar): JOB_WORKFLOW_SHA, for actions/checkout pinning.
target_dispatch_ref (or similar): branch/tag parsed from JOB_WORKFLOW_REF, for workflow dispatch.
- Preserve the existing
target_ref output only if backward compatibility requires it. If kept, prefer the dispatch-compatible branch/tag value because it is already wired into dispatch-workflow.
-
Parse the dispatch-compatible ref from JOB_WORKFLOW_REF
- Use the already-passed
JOB_WORKFLOW_REF env var.
- Extract the substring after
@, e.g. refs/heads/main from owner/repo/.github/workflows/file.yml@refs/heads/main.
- If the parsed ref is missing, fall back explicitly and log why. Avoid silently substituting
JOB_WORKFLOW_SHA for a value that will be sent to createWorkflowDispatch.
-
Wire each output to the correct consumer
- In
pkg/workflow/compiler_activation_job.go / checkout generation, use the checkout-specific SHA output for activation checkout if exact pinning is still desired.
- In
pkg/workflow/compiler_safe_outputs_config.go, inject the dispatch-specific branch/tag output into dispatch_workflow's target-ref.
- Do not pass
JOB_WORKFLOW_SHA to dispatch_workflow.cjs as target-ref.
-
Restore or replace resolver unit tests
- Reintroduce focused tests for
actions/setup/js/resolve_host_repo.cjs.
- Cover at least:
JOB_WORKFLOW_REF=owner/repo/.github/workflows/file.yml@refs/heads/main yields dispatch ref refs/heads/main.
JOB_WORKFLOW_SHA=<sha> yields checkout ref <sha>.
JOB_WORKFLOW_REF with a tag yields dispatch ref refs/tags/<tag>.
- malformed/missing
JOB_WORKFLOW_REF does not silently use SHA as the dispatch ref.
-
Add safe output integration coverage
- Add or update a compiler/runtime test showing that a workflow_call relay with
safe-outputs.dispatch-workflow injects a branch/tag ref into GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG.
- Add a
dispatch_workflow.cjs test or fixture ensuring a SHA-like target-ref is not produced by compiler-injected relay configuration.
-
Update comments and documentation
- Update comments in
pkg/workflow/compiler_activation_job.go and pkg/workflow/compiler_activation_job_test.go that currently say target_ref uses job.workflow_sha for immutable pinning.
- Update
docs/src/content/docs/reference/safe-outputs-specification.md and pkg/parser/schemas/main_workflow_schema.json if needed. The docs currently describe target-ref as "branch, tag, or SHA", but the workflow dispatch API path should document the branch/tag requirement or distinguish checkout refs from dispatch refs.
-
Validate
- Run the JavaScript tests for
actions/setup/js/resolve_host_repo.cjs and actions/setup/js/dispatch_workflow.cjs.
- Run the Go workflow compiler tests touching activation job and safe output config generation.
- Run
make agent-finish before completing, per contribution guidelines.
Suggested labels
Per scratchpad/labels.md, this should be labeled:
I did not see public component labels named workflow or actions in the current label list, so I am only applying existing labels.
Summary
dispatch-workflowsafe outputs can fail in workflow_call relay workflows because the compiler-injectedtarget-refis sourced fromneeds.activation.outputs.target_ref, andresolve_host_repo.cjscurrently sets that output toJOB_WORKFLOW_SHA.The GitHub workflow dispatch API expects
refto be a branch or tag ref. Whendispatch_workflow.cjsforwards the SHA asref, dispatch fails with:This appears to have regressed in
5a61a7c6fe9dcb35da3802a359612380147d0181/ #25697 (feat: use job.workflow_* context for host repo resolution), which is included starting inv0.68.2. Pinning the setup action back tov0.68.1makes the same workflow dispatch successfully.Impact
This breaks workflows that:
workflow_callrelay behavior,safe-outputs.dispatch-workflow, andtarget-reffor dispatching a same-repo worker workflow from the safe outputs job.The agent phase succeeds and emits
dispatch_workflowitems, but thesafe_outputsjob fails to dispatch each worker becauserefis a SHA rather than a branch/tag ref.Agent analysis
I analyzed the local source and compared the known-good
v0.68.1setup action againstv0.71.1andmain.Relevant runtime path
pkg/workflow/compiler_safe_outputs_config.goworkflow_callworkflows withsafe-outputs.dispatch-workflow, the compiler injects:target-repo: ${{ needs.activation.outputs.target_repo }}target-ref: ${{ needs.activation.outputs.target_ref }}pkg/workflow/compiler_activation_job.gotarget_ref: ${{ steps.resolve-host-repo.outputs.target_ref }}JOB_WORKFLOW_SHA: ${{ job.workflow_sha }}andJOB_WORKFLOW_REF: ${{ job.workflow_ref }}toresolve_host_repo.cjs.actions/setup/js/resolve_host_repo.cjsmainandv0.71.1, this currently does:actions/setup/js/dispatch_workflow.cjsconfig["target-ref"]is present, it unconditionally dispatches using that value:Because
target-refis now the workflow SHA,createWorkflowDispatchreceives a SHA forrefand fails withNo ref found for: <sha>.Regression point
git log -S "JOB_WORKFLOW_SHA" -- actions/setup/js/resolve_host_repo.cjsidentifies:git tag --contains 5a61a7c6feshows the change is present inv0.68.2and later.v0.68.1does not contain it.The commit replaced the older resolver, which parsed the branch/tag from
GITHUB_WORKFLOW_REF, with a simplified resolver that always emitsJOB_WORKFLOW_SHAastarget_ref. It also deletedactions/setup/js/resolve_host_repo.test.cjs, which previously covered this resolver behavior.Why using SHA is unsafe for this output
Using a SHA is useful for
actions/checkout ref:because checkout can pin to a commit. However, the sametarget_refoutput is also used bydispatch-workflowsafe outputs as the REST APIreffield.The API contract for
createWorkflowDispatchrequires a branch or tag ref; it does not accept an arbitrary workflow commit SHA in this scenario. So one output cannot safely serve both:ref, where branch/tag is required.Expected behavior
dispatch-workflowsafe outputs in a workflow_call relay should dispatch the target workflow using a branch or tag ref that exists in the target repository, typically derived fromjob.workflow_ref.For example, if:
then the dispatch
refshould berefs/heads/main(ormain, if normalized), not<commit sha>.Activation checkout can still pin to
JOB_WORKFLOW_SHAif desired, but the value forwarded todispatch_workflow.cjsmust be dispatch-compatible.Actual behavior
resolve_host_repo.cjsemits:The safe outputs handler then calls:
and the API returns:
Reproduction
Minimal workflow shape:
repo-worker.md/repo-worker.lock.ymlshould defineworkflow_dispatch.Run the orchestrator on a non-default or default branch using a setup action version containing #25697 (for example
v0.71.1). The safe outputs handler receives a compiler-injectedtarget-reffromneeds.activation.outputs.target_ref, which resolves tojob.workflow_sha, and dispatch fails withNo ref found for: <sha>.As a workaround, compiling the same workflow with:
or otherwise pinning the setup action to
v0.68.1makes dispatch succeed because the older resolver emits the branch/tag component fromGITHUB_WORKFLOW_REF.Proposed implementation plan
Please implement the following changes with a coding agent.
Split checkout ref from dispatch ref
actions/setup/js/resolve_host_repo.cjs, produce separate outputs for the two different API contracts:target_checkout_ref(or similar):JOB_WORKFLOW_SHA, foractions/checkoutpinning.target_dispatch_ref(or similar): branch/tag parsed fromJOB_WORKFLOW_REF, for workflow dispatch.target_refoutput only if backward compatibility requires it. If kept, prefer the dispatch-compatible branch/tag value because it is already wired intodispatch-workflow.Parse the dispatch-compatible ref from
JOB_WORKFLOW_REFJOB_WORKFLOW_REFenv var.@, e.g.refs/heads/mainfromowner/repo/.github/workflows/file.yml@refs/heads/main.JOB_WORKFLOW_SHAfor a value that will be sent tocreateWorkflowDispatch.Wire each output to the correct consumer
pkg/workflow/compiler_activation_job.go/ checkout generation, use the checkout-specific SHA output for activation checkout if exact pinning is still desired.pkg/workflow/compiler_safe_outputs_config.go, inject the dispatch-specific branch/tag output intodispatch_workflow'starget-ref.JOB_WORKFLOW_SHAtodispatch_workflow.cjsastarget-ref.Restore or replace resolver unit tests
actions/setup/js/resolve_host_repo.cjs.JOB_WORKFLOW_REF=owner/repo/.github/workflows/file.yml@refs/heads/mainyields dispatch refrefs/heads/main.JOB_WORKFLOW_SHA=<sha>yields checkout ref<sha>.JOB_WORKFLOW_REFwith a tag yields dispatch refrefs/tags/<tag>.JOB_WORKFLOW_REFdoes not silently use SHA as the dispatch ref.Add safe output integration coverage
safe-outputs.dispatch-workflowinjects a branch/tag ref intoGH_AW_SAFE_OUTPUTS_HANDLER_CONFIG.dispatch_workflow.cjstest or fixture ensuring a SHA-liketarget-refis not produced by compiler-injected relay configuration.Update comments and documentation
pkg/workflow/compiler_activation_job.goandpkg/workflow/compiler_activation_job_test.gothat currently saytarget_refusesjob.workflow_shafor immutable pinning.docs/src/content/docs/reference/safe-outputs-specification.mdandpkg/parser/schemas/main_workflow_schema.jsonif needed. The docs currently describetarget-refas "branch, tag, or SHA", but the workflow dispatch API path should document the branch/tag requirement or distinguish checkout refs from dispatch refs.Validate
actions/setup/js/resolve_host_repo.cjsandactions/setup/js/dispatch_workflow.cjs.make agent-finishbefore completing, per contribution guidelines.Suggested labels
Per
scratchpad/labels.md, this should be labeled:bugpriority-highI did not see public component labels named
workfloworactionsin the current label list, so I am only applying existing labels.