{"record":{"id":"1362ebd2299168af","repo":"floci-io/floci","slug":"hook-script-timed-out-after-d-seconds-s","errorCode":null,"errorMessage":"Hook script timed out after %d seconds: %s","messagePattern":"Hook script timed out after (.+?) seconds: (.+?)","errorType":"panic","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/io/github/hectorvent/floci/lifecycle/inithook/HookScriptExecutor.java","lineNumber":53,"sourceCode":"\n    void run(final Process process, final String scriptFileName) throws InterruptedException {\n        final int exitCode = waitForProcessExitCode(process, scriptFileName);\n        if (exitCode != 0) {\n            final String message = String.format(\"Hook script failed: %s exited with code %d\", scriptFileName, exitCode);\n            throw new IllegalStateException(message);\n        }\n    }\n\n    private int waitForProcessExitCode(final Process process, final String scriptFileName) throws InterruptedException {\n        try {\n            final long timeoutSeconds = initHooksConfig.timeoutSeconds();\n            final boolean finished = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);\n            if (!finished) {\n                LOG.debugv(\"Hook script exceeded timeout of {0} seconds, terminating process: {1}\", timeoutSeconds, scriptFileName);\n                terminateProcess(process, scriptFileName);\n\n                final String message = String.format(\"Hook script timed out after %d seconds: %s\", timeoutSeconds, scriptFileName);\n                throw new IllegalStateException(message);\n            }\n\n            return process.exitValue();\n        } finally {\n            if (process.isAlive()) {\n                LOG.debugv(\"Hook script process still alive during cleanup, forcing termination: {0}\", scriptFileName);\n                process.destroyForcibly();\n            }\n        }\n    }\n\n    private void terminateProcess(final Process process, final String scriptFileName) throws InterruptedException {\n        // Try a graceful shutdown first, then force termination if the process does not exit in time.\n        process.destroy();\n        if (process.isAlive()) {\n            final long shutdownGracePeriodSeconds = initHooksConfig.shutdownGracePeriodSeconds();\n            final boolean terminatedGracefully = process.waitFor(shutdownGracePeriodSeconds, TimeUnit.SECONDS);\n            if (!terminatedGracefully) {","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/floci-io/floci/blob/62ff490619e7bd3554597c28c704081b4c15add5/src/main/java/io/github/hectorvent/floci/lifecycle/inithook/HookScriptExecutor.java#L35-L71","documentation":"Thrown when an init-hook script does not finish within initHooksConfig.timeoutSeconds(). Floci waits with process.waitFor(timeout, SECONDS), logs that the timeout was exceeded, attempts graceful termination via terminateProcess, and throws IllegalStateException with the configured timeout value and script name. A finally block force-destroys the process if it is still alive.","triggerScenarios":"A hook script blocks forever: waiting on a network resource that never responds (curl without --max-time), a long sleep, a REPL prompt, reading stdin (the process inherits parent I/O), or a retry loop with no bound. Also a timeoutSeconds configuration set lower than the script legitimately needs.","commonSituations":"Scripts ported from interactive manuals that pause for input; curl/wget without timeouts in flaky networks; DNS hangs in containers; a script that waits for a containerized dependency (RDS/Lambda image pull) whose startup exceeds the configured Floci hook timeout.","solutions":["Reproduce locally by timing the script: time sh script.sh — find the hanging command","Add explicit timeouts to every network command in the hook (curl --max-time 10, wget -T 10, timeout 30 cmd)","Ensure the script never reads stdin; redirect stdin from /dev/null if needed","If the script legitimately needs longer, raise the init-hooks timeoutSeconds in Floci configuration (floci.* init hooks config in application.yml / FLOCI_* env vars)","Bound any retry loop in the script with a maximum attempt count and total wall-clock budget below the configured timeout"],"exampleFix":"# before\nuntil curl -s http://deps.local/ready; do sleep 1; done\n\n# after\nfor i in $(seq 1 30); do\n  curl -sf --max-time 5 http://deps.local/ready && exit 0\n  sleep 1\ndone\necho \"dependency not ready in 30s\" >&2\nexit 1","handlingStrategy":"validation","validationCode":"# time each hook; anything near the configured timeout is a latent failure\ntimeout 30 sh hooks/01-setup.sh; echo \"exit=$?\"  # 124 means it would time out","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never let a hook read stdin; redirect '< /dev/null' where relevant","Bound all waits/retries inside hooks to well under the configured timeoutSeconds","Raise floci init-hooks timeoutSeconds only after proving the script genuinely needs longer"],"tags":["lifecycle","init-hooks","timeout","process","startup"],"backgroundTag":null,"analyzedSha":"62ff490619e7bd3554597c28c704081b4c15add5","analyzedAt":"2026-08-14T14:25:23.764Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}