Skip to content

Bug: dispatch-workflow receives job.workflow_sha as target-ref and fails with No ref found #30403

@danquirk

Description

@danquirk

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:

  1. use workflow_call relay behavior,
  2. configure safe-outputs.dispatch-workflow, and
  3. 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

  1. 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 }}
  2. 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.
  3. 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 || "";
  1. 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:

target_ref=<commit sha>

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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:

  • bug
  • priority-high

I did not see public component labels named workflow or actions in the current label list, so I am only applying existing labels.

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions