Skip to content

checkout: multi fails every run in Post Checkout runtime repositories — finish() deletes the composite action.yml the post hook still needs #75199

Open

Description

@tgrace-vl

Summary

Any workflow using checkout: with multi: true finishes every run with a failed job, even when the agent, the safe outputs and the pull request all succeed. The failure is in the Post Checkout runtime repositories (N) step:

Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under
'/home/runner/work/<repo>/<repo>/.gh-aw/checkouts/1'.
Did you forget to run actions/checkout before running your local action?

Both the agent job and the safe_outputs job fail this way in the same run. safe_outputs never invokes the agent, which rules out anything the agent does to the workspace.

Root cause

actions/setup/js/checkout_dynamic.cjs:

  1. prepare() writes a generated composite action to .gh-aw/checkouts/<index>/action.yml, whose steps are uses: actions/checkout@<sha>.
  2. The compiled job runs it with uses: ./.gh-aw/checkouts/<index>.
  3. actions/checkout registers a post hook. Because it is nested inside a composite action, the runner must re-resolve the composite's action.yml when the post phase runs at the end of the job.
  4. finish() deletes that file unconditionally:
const actionDir = workspacePath(config.workspace, `.gh-aw/checkouts/${config.index}`);
try {
  fs.unlinkSync(path.join(actionDir, "action.yml"));
  fs.rmdirSync(actionDir);
} catch (error) {
  throw new Error("Unable to remove the temporary runtime checkout action", { cause: error });
}
  1. The post phase fires, cannot resolve the path, and the job is marked failed.

The deletion itself looks intentional and correct — the generated action.yml carries a token: reference and should not outlive the checkout. The problem is that it is unconditional, with nothing accounting for the post hook it strands.

This is the same class of problem as #51448 (Post Setup Scripts on a since-removed composite path), but a different instance: that one was the evals job's actions/setup, this one is the generated runtime-checkout action, and it reproduces on every run rather than intermittently.

Versions

Not a regression, and not fixed on the current release. The code above is identical in v0.92.0 and v1.0.1 (actions/setup/js/checkout_dynamic.cjs, finish()), so the bug should affect every version in that range.

Reproduction

A workflow_dispatch workflow with a second checkout entry:

checkout:
  - fetch-depth: 1
  - repo: ${{ inputs.repository }}
    multi: true
    allowed-repos: my-org/*
    max-repos: 1
    path: target
    github-token: ${{ secrets.A_READ_ONLY_PAT }}

Compile and dispatch. The agent runs, the safe output lands, and both agent and safe_outputs still end red on Post Checkout runtime repositories (1).

Impact

The run's conclusion no longer carries information: a fully successful run and a genuinely broken one both show red. For a scheduled or frequently dispatched workflow that trains people to ignore the status, which is the signal you need to notice a real failure.

Possible fixes

  • Keep a minimal no-op action.yml in place (no token: reference) instead of removing the file, so the post phase resolves and does nothing.
  • Or have prepare() emit steps that do not register a post hook, so there is nothing to strand.
  • Or restore the file for the post phase and remove the directory after the job.

Activity

  1. locked and limited conversation to collaborators on Nov 5, 2026
  2. unlocked this conversation on Nov 5, 2026
  3. niebloomj commented on Nov 5, 2026

    @niebloomj

    Additional reproduction evidence:

    • Two consecutive natural runs using checkout: multi: true both failed in Post Checkout runtime repositories (1).
    • In one run, the agent completed and safe-output processing passed; in the other, the agent hit the same post-checkout failure, with an additional independent safe-output processing failure.
    • A preceding green run used a workflow revision without a runtime checkout: entry, so it does not show that the multi-checkout path worked.

    The repeated result points to a lifecycle failure on the multi-checkout path rather than a sporadic engine failure: the temporary composite manifest is removed before the checkout post-hook can reload it.

  4. locked and limited conversation to collaborators on Nov 5, 2026
  5. unlocked this conversation on Nov 5, 2026
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