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

  1. Run the hook manually with the same interpreter Floci uses (sh script.sh / python3 script.py) and fix the failing command
  2. Add 'set -euo pipefail' (shell) or explicit exit codes (Python) so failures happen at the real failing line and are visible
  3. Verify every binary the script invokes exists in the container/host where Floci runs
  4. If the failure is an ordering issue, make the script wait/retry on the dependency or move it to a later hook stage
  5. 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

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


AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14). Data as JSON: /api/errors/073ffb02ea5043a1. Report an issue: GitHub.