docker/compose · error

service %q pre_start[%d] wait error: %s

Error message

service %q pre_start[%d] wait error: %s

What it means

After a pre_start hook container exits, compose reads the container wait response. If the daemon reports an error for the wait itself (res.Error set — e.g. the container died abnormally), the daemon's message is surfaced with the service name and hook index. This is distinct from a clean non-zero exit: the container failed at the runtime level.

Source

Thrown at pkg/compose/pre_start.go:251

					return err
				}
			default:
			}
			return preStartResultErr(serviceName, index, res)
		case err := <-errCh:
			if err != nil {
				return err
			}
			// nil on Error: stream closed cleanly. Disable this case so a
			// closed channel can't fire repeatedly.
			errCh = nil
		}
	}
}

func preStartResultErr(serviceName string, index int, res container.WaitResponse) error {
	if res.Error != nil {
		return fmt.Errorf("service %q pre_start[%d] wait error: %s", serviceName, index, res.Error.Message)
	}
	if res.StatusCode != 0 {
		return fmt.Errorf("service %q pre_start[%d] exited with code %d", serviceName, index, res.StatusCode)
	}
	return nil
}

// streamPreStartLogs returns a channel that is closed once the hook log stream
// has been fully drained (or never opened). Callers must wait on it before
// returning so the goroutine cannot outlive the hook.
func (s *composeService) streamPreStartLogs(ctx context.Context, containerID string, service types.ServiceConfig, index int, listener api.ContainerEventListener) <-chan struct{} {
	done := make(chan struct{})
	if listener == nil {
		close(done)
		return done
	}
	source := fmt.Sprintf("%s pre_start[%d] ->", service.Name, index)
	logs, err := s.apiClient().ContainerLogs(ctx, containerID, client.ContainerLogsOptions{

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. docker inspect the hook container: check OOMKilled, ExitCode, and the wait error detail
  2. Raise mem_limit/resources for the hook or reduce the hook's memory footprint
  3. Check daemon logs (journalctl -u docker) for the wait error cause
  4. Fix whatever the daemon's Error.Message names (it is included verbatim in the compose error)
Defensive patterns

Strategy: try-catch

Try / catch

if err := upWithPreStart(ctx, project); err != nil {
    if strings.Contains(err.Error(), "pre_start") && strings.Contains(err.Error(), "wait error") {
        // daemon-side failure: inspect hook container for OOMKilled/kill cause
    }
}

Prevention

When it happens

Trigger: ContainerWait returning a WaitResponse with Error set: hook container OOM-killed, killed by the daemon, or an engine-side error while the hook ran.

Common situations: Memory-hungry init scripts exceeding container memory limits; cgroup/ulimit kills; daemon instability during hook execution.

Related errors


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