Skip to content

Cached Copilot CLI activation skips the /usr/local/bin wrapper, so agent spawn fails with ENOENT #51843

Description

@heiskr

👋 Copilot here, on behalf of @heiskr.

When install_copilot_cli.sh finds a usable Copilot CLI in the toolcache, it adds the cache directory to PATH and GITHUB_PATH but never creates /usr/local/bin/copilot. The agent step invokes the binary by that absolute path, so the run fails immediately with ENOENT.

Users see this as "the copilot engine terminated before producing output", which points at the engine or the network instead of a missing binary.

Root cause

In actions/setup/sh/install_copilot_cli.sh, activate_cached_copilot_bin() (lines 481 to 507 on main):

if [ -n "${GITHUB_PATH:-}" ]; then
  echo "  Exporting ${cached_copilot_dir} to GITHUB_PATH (${GITHUB_PATH})"
  echo "$cached_copilot_dir" >> "${GITHUB_PATH}"
  return 0          # <-- returns before the wrapper is installed
fi

# Outside GitHub Actions there is no GITHUB_PATH file, so install a small wrapper
echo "  GITHUB_PATH not set — installing wrapper at ${INSTALL_DIR}/copilot"
...
maybe_sudo install -m 0755 "$wrapper_path" "${INSTALL_DIR}/copilot"

GITHUB_PATH is always set under GitHub Actions, so the wrapper branch is unreachable in exactly the environment that needs it. Meanwhile pkg/constants/constants.go hardcodes the invocation target:

// CopilotBinaryPath is the path to the Copilot CLI binary inside AWF containers.
const CopilotBinaryPath = "/usr/local/bin/copilot"

The cache-miss path runs install into $INSTALL_DIR (/usr/local/bin), which is why a cold cache works and a warm cache does not.

Failure signature

[copilot-harness] pre-flight: command not found: /usr/local/bin/copilot (F_OK check failed — binary does not exist at this path)
[copilot-harness] attempt 1: failed to start process '/usr/local/bin/copilot': spawn /usr/local/bin/copilot ENOENT
[copilot-harness] attempt 1 failed: exitCode=1 failureClass=no_output isCAPIError400=false isCAPIQuotaExceededError=false isMCPPolicyError=false

Reproduction

Trigger it with any runner whose toolcache holds a copilot-cli entry that is inside the compat window and younger than the 14-day TTL. The install step logs the cache-hit branch:

Selected best cached version: 1.0.77 at /opt/hostedtoolcache/copilot-cli/1.0.77/x64/bin/copilot
Activating cached Copilot CLI from /opt/hostedtoolcache/copilot-cli/1.0.77/x64/bin/copilot...
  Prepended /opt/hostedtoolcache/copilot-cli/1.0.77/x64/bin to PATH
  Exporting ... to GITHUB_PATH
✓ Copilot CLI installation complete (cached)

An expired or absent cache takes the working branch instead:

  Skipping candidate (cache expired and not max-agent: 1.0.56 != 1.0.78)
  No compatible toolcache entry found
Installing binary to /usr/local/bin...
✓ Copilot CLI installation complete

I saw five consecutive failures over about 26 hours on GH_AW_COMPILED_VERSION: v0.84.3 with a 6-day-old 1.0.77 cache against a 1.0.21..1.0.78 compat window. The early return is still present on main.

Why it looks intermittent

It depends on which runner image you land on. Workflows recover on their own once the cached entry ages past the TTL, which makes it read as a transient engine or infrastructure problem. It is deterministic given a warm cache.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions