docker/cli · error

unrecognized service mode

Error message

unrecognized service mode

What it means

In `progress.initializeUpdater()` (progress.go:234-253), the function inspects the service's `Spec.Mode` field to select the appropriate progress updater (replicated, global, replicated-job, or global-job). If none of the four mode sub-structs (`Replicated`, `Global`, `ReplicatedJob`, `GlobalJob`) is non-nil, the function falls through to line 253 and returns this error. Under normal operation, a Docker Swarm service always has exactly one mode set. This error indicates a corrupt or unexpected service spec — possibly from a newer engine API with an unrecognized mode, or data corruption.

Solutions

  1. Verify the service mode using `docker service inspect --format '{{.Spec.Mode}}' <service>` — if the mode is empty or unrecognized, the service spec is corrupt
  2. Upgrade the Docker CLI to match or exceed the Docker Engine version
  3. Recreate the service with a known mode (`--mode replicated`, `global`, `replicated-job`, or `global-job`)

Example fix

# diagnose the service mode
docker service inspect --format '{{json .Spec.Mode}}' myservice

# if mode is empty/corrupt, recreate the service
docker service rm myservice
docker service create --name myservice --mode replicated --replicas 3 --image nginx
Defensive patterns

Strategy: try-catch

Validate before calling

// Before calling ServiceProgress, verify the service mode is recognized
func isRecognizedMode(mode swarm.ServiceMode) bool {
    return mode.Replicated != nil || mode.Global != nil ||
        mode.ReplicatedJob != nil || mode.GlobalJob != nil
}

// Usage:
// res, _ := apiClient.ServiceInspect(ctx, svcID, opts)
// if !isRecognizedMode(res.Service.Spec.Mode) {
//     return errors.New("service has no recognized mode")
// }

Type guard

// Type guard for swarm.ServiceMode
defunc hasKnownServiceMode(m swarm.ServiceMode) bool {
    return m.Replicated != nil || m.Global != nil ||
        m.ReplicatedJob != nil || m.GlobalJob != nil
}

Try / catch

// Wrap ServiceProgress in error handling
err := progress.ServiceProgress(ctx, apiClient, svcID, progressWriter)
if err != nil && strings.Contains(err.Error(), "unrecognized service mode") {
    // Service spec is corrupt or CLI version is too old for this mode
    log.Printf("service %s has an unrecognized mode; inspect manually: %v", svcID, err)
    // Fall back to non-blocking monitoring
    return nil
}

Prevention

When it happens

Trigger: Calling `ServiceProgress()` on a service whose `Spec.Mode` has all four sub-structs nil. This is an internal invariant violation — it should not happen with services created by the standard Docker API. Could theoretically occur if the swarm store is corrupted, or if a future API version introduces a new mode that this CLI version does not recognize.

Common situations: Version skew: an older CLI connected to a newer Docker Engine that supports a service mode the CLI does not know about. Or a manually constructed/corrupted swarm service object passed to the progress API.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/5627b3e3c2d76792. Report an issue: GitHub.

Appendix: source

Thrown at cli/command/service/progress/progress.go:253

	if service.Spec.Mode.Replicated != nil && service.Spec.Mode.Replicated.Replicas != nil {
		return &replicatedProgressUpdater{
			progressOut: progressOut,
		}, nil
	}
	if service.Spec.Mode.Global != nil {
		return &globalProgressUpdater{
			progressOut: progressOut,
		}, nil
	}
	if service.Spec.Mode.ReplicatedJob != nil {
		return newReplicatedJobProgressUpdater(service, progressOut), nil
	}
	if service.Spec.Mode.GlobalJob != nil {
		return &globalJobProgressUpdater{
			progressOut: progressOut,
		}, nil
	}
	return nil, errors.New("unrecognized service mode")
}

func writeOverallProgress(progressOut progress.Output, numerator, denominator int, rollback bool) {
	if rollback {
		progressOut.WriteProgress(progress.Progress{
			ID:     "overall progress",
			Action: fmt.Sprintf("rolling back update: %d out of %d tasks", numerator, denominator),
		})
		return
	}
	progressOut.WriteProgress(progress.Progress{
		ID:     "overall progress",
		Action: fmt.Sprintf("%d out of %d tasks", numerator, denominator),
	})
}

func truncError(errMsg string) string {
	// Remove newlines from the error, which corrupt the output.

View on GitHub (pinned to 4f84911bfe)