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
- docker inspect the hook container: check OOMKilled, ExitCode, and the wait error detail
- Raise mem_limit/resources for the hook or reduce the hook's memory footprint
- Check daemon logs (journalctl -u docker) for the wait error cause
- 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
- Set generous memory limits for hook containers running heavy init scripts
- Inspect the hook container (OOMKilled, ExitCode) whenever a wait error appears
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
- service %q pre_start[%d]: per_replica is not yet supported;
- service %q pre_start[%d] exited with code %d
- --wait cannot be combined with --abort-on-container-exit, --
- --wait cannot be combined with --abort-on-container-exit, --
- %s hook exited with status %d
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/abc7486ffce10340.
Report an issue: GitHub.