exec: "ls -la": executable file not found in $PATH (terragrunt hook execute)
Fixes Terragrunt hooks failing with 'executable file not found in $PATH' by passing execute as a list. Use when a hook fails even though the command works in your shell. Not for hooks that run but exit non-zero.
TL;DR: execute takes a LIST, not a shell string. execute = ["ls -la"] looks for a binary literally named ls -la and fails with "executable file not found". Split it into ["ls", "-la"], or use ["bash", "-c", "..."] when you genuinely need a shell.
Error: hook "preflight" failed: exec: "ls -la": executable file not found in $PATHSteps
- Look at the
executeattribute: is it a single string with spaces?
Expected: you find execute = ["ls -la"] or similar.
- Split it:
terraform {
before_hook "preflight" {
commands = ["plan"]
execute = ["bash", "-c", "set -euo pipefail; ./scripts/preflight.sh"]
}
}Expected: binary and args are separate list elements.
- If the hook fails silently, remove
suppress_stdout = truewhile debugging (it hides the hook's stdout, where diagnostics usually go).
Expected: you can see the hook's actual output.
- Re-run.
Expected: the hook executes.
When this applies
- Hook fails with
executable file not found in $PATHnaming a string with spaces. - A command that works in your shell fails as a hook.
When it doesn't apply
- The hook RUNS but exits non-zero: that's the script's own failure; read its output (check
suppress_stdoutfirst). commandsdoesn't list the subcommand you're running: the hook never fires at all, which looks different (no error).
Tool versions
All Terragrunt versions (verified on 1.1.3).
Why it happens
Terragrunt execs the list directly without a shell, so the first element must be a real binary. A single string is treated as one binary name, spaces included. There is no shell splitting, globbing, or piping unless you invoke one explicitly.
Edge cases
- Hooks run from the module directory by default (a temp
.terragrunt-cachecopy for remote sources), NOT the config directory; anchor scripts withget_terragrunt_dir()instead of relative paths. - Each
executeis its own process: aTF_VAR_*exported by one hook is not visible to the next.