erlef/setup-beam successfully installs OTP/Elixir before the agent runs.
However, the generated AWF/Copilot shell setup does not preserve the setup-beam PATH.
Instead, it reconstructs PATH from RUNNER_TOOL_CACHE with a depth-limited find.
That exposes an Elixir/mix binary, but not a working erl executable.
As a result, mix commands fail because Elixir tries to exec erl and cannot find it.
The failure is not that setup-beam failed. setup-beam successfully installed and verified OTP/Elixir
under:
/home/runner/work/_temp/.setup-beam/otp/bin/erl
/home/runner/work/_temp/.setup-beam/elixir/bin/mix
The bug is that the AWF/Copilot sandbox path bootstrap does not preserve those activated setup-beam
paths.
Relevant source:
-
/private/tmp/gh-aw-src/pkg/workflow/nodejs.go:189: GetNpmBinPathSetup scans only $RUNNER_TOOL_CACHE
-
/private/tmp/gh-aw-src/pkg/workflow/copilot_engine_execution.go:397: Copilot prepends
GetNpmBinPathSetup() inside AWF
-
/private/tmp/gh-aw-src/pkg/workflow/runtime_definitions.go:69: runtimes.elixir uses erlef/setup-beam
-
/private/tmp/gh-aw-src/pkg/workflow/runtime_step_generator.go:24: comments assume AWF preserves setup
action paths via AWF_HOST_PATH
-
/private/tmp/gh-aw-src/pkg/workflow/runtime_setup_test.go:393: Elixir tests only assert setup-beam is
emitted, not that erl is reachable
GetNpmBinPathSetup currently does this:
export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\n' ':')$PATH"
That is insufficient for setup-beam because setup-beam’s active install is copied to $RUNNER_TEMP/.setup-
beam/..., then core.addPath(/bin) is called. The canonical erl is at $INSTALL_DIR_FOR_OTP/bin/
erl, not necessarily inside $RUNNER_TOOL_CACHE.
Increasing find -maxdepth is probably not the right fix. The agent found erl.src / erts-* artifacts in /
opt/hostedtoolcache, but the working executable verified by setup-beam was $INSTALL_DIR_FOR_OTP/bin/erl.
Secondary Issues
runtimes.elixir models only:
Commands: []string{"elixir", "mix", "iex"}
It should probably also include erl, erlc, and escript. That is not the direct mix test failure, but it
makes runtime detection incomplete.
The tests are too shallow. Current coverage proves “setup-beam step is generated,” but not “AWF shell can
resolve mix and erl after setup-beam.”
Proposed Fix
Best targeted fix: extend the AWF runtime PATH bootstrap to include setup-beam’s documented install dirs.
Something like:
for GH_AW_BEAM_DIR in
"${INSTALL_DIR_FOR_OTP:-}"
"${INSTALL_DIR_FOR_ELIXIR:-}"
"${INSTALL_DIR_FOR_GLEAM:-}"
"${INSTALL_DIR_FOR_REBAR3:-}"
do
if [ -n "$GH_AW_BEAM_DIR" ] && [ -d "$GH_AW_BEAM_DIR/bin" ]; then
export PATH="$GH_AW_BEAM_DIR/bin:$PATH"
fi
done
This should live either in GetNpmBinPathSetup after the toolcache scan, or preferably in a renamed/
generalized helper such as GetAWFRuntimePathSetup.
Better architectural fix: capture the post-setup host PATH before sudo -E awf and restore it inside AWF.
That matches the comments about AWF_HOST_PATH, but it is broader and may need more compatibility testing.
Tests To Add
-
Add a unit test for the path helper:
- create fake $RUNNER_TOOL_CACHE
- create fake $INSTALL_DIR_FOR_OTP/bin/erl
- create fake $INSTALL_DIR_FOR_ELIXIR/bin/mix
- run the helper in bash
- assert command -v erl and command -v mix resolve to setup-beam dirs
-
Add a compile test for an Elixir runtime workflow:
- runtimes.elixir
- tools.bash: ["mix:*"]
- assert the generated AWF command includes setup-beam install-dir PATH handling
-
Expand runtime_definitions.go Elixir commands:
- elixir
- mix
- iex
- erl
- erlc
- escript
Similar Issues Found
erlef/setup-beam successfully installs OTP/Elixir before the agent runs.
However, the generated AWF/Copilot shell setup does not preserve the setup-beam PATH.
Instead, it reconstructs PATH from RUNNER_TOOL_CACHE with a depth-limited find.
That exposes an Elixir/mix binary, but not a working erl executable.
As a result, mix commands fail because Elixir tries to exec erl and cannot find it.
The failure is not that setup-beam failed. setup-beam successfully installed and verified OTP/Elixir
under:
/home/runner/work/_temp/.setup-beam/otp/bin/erl
/home/runner/work/_temp/.setup-beam/elixir/bin/mix
The bug is that the AWF/Copilot sandbox path bootstrap does not preserve those activated setup-beam
paths.
Relevant source:
/private/tmp/gh-aw-src/pkg/workflow/nodejs.go:189: GetNpmBinPathSetup scans only $RUNNER_TOOL_CACHE
/private/tmp/gh-aw-src/pkg/workflow/copilot_engine_execution.go:397: Copilot prepends
GetNpmBinPathSetup() inside AWF
/private/tmp/gh-aw-src/pkg/workflow/runtime_definitions.go:69: runtimes.elixir uses erlef/setup-beam
/private/tmp/gh-aw-src/pkg/workflow/runtime_step_generator.go:24: comments assume AWF preserves setup
action paths via AWF_HOST_PATH
/private/tmp/gh-aw-src/pkg/workflow/runtime_setup_test.go:393: Elixir tests only assert setup-beam is
emitted, not that erl is reachable
GetNpmBinPathSetup currently does this:
export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\n' ':')$PATH"
That is insufficient for setup-beam because setup-beam’s active install is copied to $RUNNER_TEMP/.setup-
beam/..., then core.addPath(/bin) is called. The canonical erl is at $INSTALL_DIR_FOR_OTP/bin/
erl, not necessarily inside $RUNNER_TOOL_CACHE.
Increasing find -maxdepth is probably not the right fix. The agent found erl.src / erts-* artifacts in /
opt/hostedtoolcache, but the working executable verified by setup-beam was $INSTALL_DIR_FOR_OTP/bin/erl.
Secondary Issues
runtimes.elixir models only:
Commands: []string{"elixir", "mix", "iex"}
It should probably also include erl, erlc, and escript. That is not the direct mix test failure, but it
makes runtime detection incomplete.
The tests are too shallow. Current coverage proves “setup-beam step is generated,” but not “AWF shell can
resolve mix and erl after setup-beam.”
Proposed Fix
Best targeted fix: extend the AWF runtime PATH bootstrap to include setup-beam’s documented install dirs.
Something like:
for GH_AW_BEAM_DIR in
"${INSTALL_DIR_FOR_OTP:-}"
"${INSTALL_DIR_FOR_ELIXIR:-}"
"${INSTALL_DIR_FOR_GLEAM:-}"
"${INSTALL_DIR_FOR_REBAR3:-}"
do
if [ -n "$GH_AW_BEAM_DIR" ] && [ -d "$GH_AW_BEAM_DIR/bin" ]; then
export PATH="$GH_AW_BEAM_DIR/bin:$PATH"
fi
done
This should live either in GetNpmBinPathSetup after the toolcache scan, or preferably in a renamed/
generalized helper such as GetAWFRuntimePathSetup.
Better architectural fix: capture the post-setup host PATH before sudo -E awf and restore it inside AWF.
That matches the comments about AWF_HOST_PATH, but it is broader and may need more compatibility testing.
Tests To Add
Add a unit test for the path helper:
Add a compile test for an Elixir runtime workflow:
Expand runtime_definitions.go Elixir commands:
Similar Issues Found
gh-aw PR fix: use absolute node path in AWF command to fix
node: command not foundon GPU runners + regenerate stale lock files #26427 fixed a very similar class of bug: node: command not found inside AWF because sudostripped PATH. The fix captured an absolute Node path before AWF.
fix: use absolute node path in AWF command to fix
node: command not foundon GPU runners + regenerate stale lock files #26427gh-aw PR Require RUNNER_TOOL_CACHE for tool-cache discovery #40157 introduced the current RUNNER_TOOL_CACHE-only discovery behavior.
Require RUNNER_TOOL_CACHE for tool-cache discovery #40157
gh-aw PR fix(detection): prepend npm PATH setup to threat-detect command so engine CLIs are found in AWF chroot #40876 fixed threat detection failing to find engine CLIs in AWF chroot by prepending
GetNpmBinPathSetup.
fix(detection): prepend npm PATH setup to threat-detect command so engine CLIs are found in AWF chroot #40876
erlef/setup-beam documents that INSTALL_DIR_FOR_OTP/bin contains erl, and its source calls
core.addPath(/bin) plus core.exportVariable(INSTALL_DIR_FOR_OTP, ...).
https://github.com/erlef/setup-beam#environment-variables
https://github.com/erlef/setup-beam/blob/main/src/setup-beam.js