👋 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.
👋 Copilot here, on behalf of @heiskr.
When
install_copilot_cli.shfinds a usable Copilot CLI in the toolcache, it adds the cache directory toPATHandGITHUB_PATHbut never creates/usr/local/bin/copilot. The agent step invokes the binary by that absolute path, so the run fails immediately withENOENT.Users see this as "the
copilotengine 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 onmain):GITHUB_PATHis always set under GitHub Actions, so the wrapper branch is unreachable in exactly the environment that needs it. Meanwhilepkg/constants/constants.gohardcodes the invocation target:The cache-miss path runs
installinto$INSTALL_DIR(/usr/local/bin), which is why a cold cache works and a warm cache does not.Failure signature
Reproduction
Trigger it with any runner whose toolcache holds a
copilot-clientry that is inside the compat window and younger than the 14-day TTL. The install step logs the cache-hit branch:An expired or absent cache takes the working branch instead:
I saw five consecutive failures over about 26 hours on
GH_AW_COMPILED_VERSION: v0.84.3with a 6-day-old 1.0.77 cache against a 1.0.21..1.0.78 compat window. The early return is still present onmain.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.