floci-io/floci · error · IllegalStateException
Hook script failed: %s exited with code %d
Error message
Hook script failed: %s exited with code %d
What it means
Thrown by Floci's init-hook executor when a lifecycle hook script (shell or Python) runs to completion but exits with a non-zero status code. Floci runs user-supplied hook scripts from the configured hook directory during emulator startup; a non-zero exit is treated as a hard startup failure via IllegalStateException. The message names the offending script file and its exit code.
Source
Thrown at src/main/java/io/github/hectorvent/floci/lifecycle/inithook/HookScriptExecutor.java:40
public void run(final File scriptFile) throws IOException, InterruptedException {
run(scriptFile.getParentFile(), scriptFile.getName());
}
public void run(final File hookDirectory, final String scriptFileName) throws IOException, InterruptedException {
final String command = scriptFileName.endsWith(".py") ? "python3" : initHooksConfig.shellExecutable();
LOG.debugv("Executing hook script {0} via {1}", scriptFileName, command);
// Inherit parent I/O so script output is streamed directly and does not block on unconsumed buffers.
final Process process = new ProcessBuilder(command, scriptFileName).directory(hookDirectory).inheritIO().start();
run(process, scriptFileName);
}
void run(final Process process, final String scriptFileName) throws InterruptedException {
final int exitCode = waitForProcessExitCode(process, scriptFileName);
if (exitCode != 0) {
final String message = String.format("Hook script failed: %s exited with code %d", scriptFileName, exitCode);
throw new IllegalStateException(message);
}
}
private int waitForProcessExitCode(final Process process, final String scriptFileName) throws InterruptedException {
try {
final long timeoutSeconds = initHooksConfig.timeoutSeconds();
final boolean finished = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);
if (!finished) {
LOG.debugv("Hook script exceeded timeout of {0} seconds, terminating process: {1}", timeoutSeconds, scriptFileName);
terminateProcess(process, scriptFileName);
final String message = String.format("Hook script timed out after %d seconds: %s", timeoutSeconds, scriptFileName);
throw new IllegalStateException(message);
}
return process.exitValue();
} finally {
if (process.isAlive()) {View on GitHub (pinned to 62ff490619)
Solutions
- Run the hook manually with the same interpreter Floci uses (sh script.sh / python3 script.py) and fix the failing command
- Add 'set -euo pipefail' (shell) or explicit exit codes (Python) so failures happen at the real failing line and are visible
- Verify every binary the script invokes exists in the container/host where Floci runs
- If the failure is an ordering issue, make the script wait/retry on the dependency or move it to a later hook stage
- If the hook is optional, guard failures with explicit 'exit 0' after logging, or remove it from the hooks directory
Example fix
# before (last command's failure propagates silently or unexpected code) curl http://localhost:4566/_localstack/health # after (explicit, logged, deliberate exit code) if ! curl -sf http://localhost:4566/ > /dev/null; then echo "hook: emulator not reachable" >&2 exit 1 fi
Defensive patterns
Strategy: validation
Validate before calling
# verify the hook passes before letting Floci run it sh -n hooks/01-setup.sh && sh hooks/01-setup.sh; echo "exit=$?"
Prevention
- Run every hook manually with the same interpreter (sh/python3) before adding it to the hooks directory
- Start every shell hook with 'set -euo pipefail' and end Python hooks with explicit sys.exit codes
- Make every network call in a hook use a bounded timeout (curl --max-time, timeout(1))
When it happens
Trigger: A file in the Floci init-hooks directory (e.g. *.sh or *.py) exits non-zero when the emulator starts. Typical cases: a shell script missing 'set -e' that fails on its last command, a python3 script raising an uncaught exception, a script referencing tools absent from the image (awscli, curl), or a script returning the exit code of a failed curl/health check against a service that is not up yet.
Common situations: Custom provisioning hooks written for one environment breaking in another (missing binary, different shell), scripts that assume network access in an offline CI runner, Python scripts that work under 'python' but the executor invokes 'python3' (or vice versa), and hooks that depend on services Floci starts later so ordering assumptions break.
Related errors
- Hook script timed out after %d seconds: %s
- Boot hook execution interrupted
- Boot hook execution failed
- floci.storage.efs owner-uid and owner-gid must be set togeth
- Persistent storage path '" + root.toAbsolutePath() + "' is n
AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14).
Data as JSON: /api/errors/073ffb02ea5043a1.
Report an issue: GitHub.