{"record":{"id":"7f1a92c6ccf96ccf","repo":"docker/compose","slug":"s-hook-exited-with-status-d","errorCode":null,"errorMessage":"%s hook exited with status %d","messagePattern":"(.+?) hook exited with status (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/compose/hook.go","lineNumber":94,"sourceCode":"\t\treturn err\n\t}\n\tdefer attach.Close()\n\n\tif service.Tty {\n\t\t_, err = io.Copy(wOut, attach.Reader)\n\t} else {\n\t\t_, err = stdcopy.StdCopy(wOut, wOut, attach.Reader)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tinspected, err := s.apiClient().ExecInspect(ctx, exec.ID, client.ExecInspectOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\tif inspected.ExitCode != 0 {\n\t\treturn fmt.Errorf(\"%s hook exited with status %d\", service.Name, inspected.ExitCode)\n\t}\n\treturn nil\n}\n\nfunc (s *composeService) runWaitExec(ctx context.Context, execID string, service types.ServiceConfig, listener api.ContainerEventListener) error {\n\t_, err := s.apiClient().ExecStart(ctx, execID, client.ExecStartOptions{\n\t\tDetach: listener == nil,\n\t\tTTY:    service.Tty,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// We miss a ContainerExecWait API\n\ttick := time.NewTicker(100 * time.Millisecond)\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/docker/compose/blob/ddc4b044b62e9f715212ea4143fa830fac76382f/pkg/compose/hook.go#L76-L112","documentation":"compose runs a service lifecycle hook by creating an exec in the running container, streaming its output, then calling ExecInspect. If the hook process finished with a non-zero exit code, this error reports the service name and the hook's exit status. It is compose's way of gating service startup/teardown on hook success.","triggerScenarios":"docker compose up (or run) on a service with a hook whose command returns non-zero; after the attached exec's output is drained via StdCopy, ExecInspect(ctx, exec.ID) returns ExitCode != 0.","commonSituations":"Hook script runs a readiness check (e.g. pg_isready, curl) against a dependency that never becomes ready; hook binary missing from the image (127); script lacks +x or wrong path (126); hook logic intentionally exits 1 on validation failure.","solutions":["Reproduce manually: docker compose exec <service> <hook-command>; echo $? to see the real exit code and stderr","Fix the hook command/script so it exits 0 on success (shebang, executable bit, absolute paths)","If the hook waits on a dependency, add bounded retry inside the hook instead of failing immediately","Ensure any binary the hook invokes exists in the service image (build a custom image if needed)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := compose.Up(ctx, project, opts); err != nil {\n    if strings.Contains(err.Error(), \"hook exited with status\") {\n        // hook failure: surface the service name + status, point user at hook logs\n    }\n}","preventionTips":["Test every hook command manually in the service image before adding it to compose","Keep hooks idempotent and give them internal bounded retries for dependencies","Use absolute paths and ensure binaries exist in the hook's execution environment"],"tags":["hooks","lifecycle","exec","exit-code"],"backgroundTag":null,"analyzedSha":"ddc4b044b62e9f715212ea4143fa830fac76382f","analyzedAt":"2026-08-15T13:31:42.319Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}