docker/compose · error

service %q pre_start[%d] exited with code %d

Error message

service %q pre_start[%d] exited with code %d

What it means

The pre_start hook container finished cleanly but with a non-zero status code. Compose reports service, hook index, and exit code, and gates the service from starting. This is the pre_start analogue of a failing init container in other orchestrators.

Source

Thrown at pkg/compose/pre_start.go:254

			}
			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{
		ShowStdout: true,
		ShowStderr: true,
		Follow:     true,

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Reproduce the hook manually in a container with the same image/volumes and inspect its output
  2. Fix the hook command/entrypoint (absolute paths, executable bit, correct working dir)
  3. Make migrations idempotent so re-runs succeed after partial failure
  4. Ensure the hook image actually contains the binaries the command references
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(), "exited with code") {
        // re-run the hook manually in the same image/volumes to debug
    }
}

Prevention

When it happens

Trigger: runPreStartHook's wait returns StatusCode != 0 with no daemon error: the hook command itself failed (missing binary, bad script, failed migration, etc.).

Common situations: Migration script failing on schema conflict; hook image missing the tool it invokes; wrong working_dir/command in the pre_start entry; script exiting on first error via set -e.

Related errors


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