Skip to content

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

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions