docker/compose · error

%s hook exited with status %d

Error message

%s hook exited with status %d

What it means

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.

Source

Thrown at pkg/compose/hook.go:94

		return err
	}
	defer attach.Close()

	if service.Tty {
		_, err = io.Copy(wOut, attach.Reader)
	} else {
		_, err = stdcopy.StdCopy(wOut, wOut, attach.Reader)
	}
	if err != nil {
		return err
	}

	inspected, err := s.apiClient().ExecInspect(ctx, exec.ID, client.ExecInspectOptions{})
	if err != nil {
		return err
	}
	if inspected.ExitCode != 0 {
		return fmt.Errorf("%s hook exited with status %d", service.Name, inspected.ExitCode)
	}
	return nil
}

func (s *composeService) runWaitExec(ctx context.Context, execID string, service types.ServiceConfig, listener api.ContainerEventListener) error {
	_, err := s.apiClient().ExecStart(ctx, execID, client.ExecStartOptions{
		Detach: listener == nil,
		TTY:    service.Tty,
	})
	if err != nil {
		return err
	}

	// We miss a ContainerExecWait API
	tick := time.NewTicker(100 * time.Millisecond)
	for {
		select {
		case <-ctx.Done():

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Reproduce manually: docker compose exec <service> <hook-command>; echo $? to see the real exit code and stderr
  2. Fix the hook command/script so it exits 0 on success (shebang, executable bit, absolute paths)
  3. If the hook waits on a dependency, add bounded retry inside the hook instead of failing immediately
  4. Ensure any binary the hook invokes exists in the service image (build a custom image if needed)
Defensive patterns

Strategy: try-catch

Try / catch

if err := compose.Up(ctx, project, opts); err != nil {
    if strings.Contains(err.Error(), "hook exited with status") {
        // hook failure: surface the service name + status, point user at hook logs
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/7f1a92c6ccf96ccf. Report an issue: GitHub.