docker/cli · error

invalid restart policy: maximum retry count cannot be…

Error message

invalid restart policy: maximum retry count cannot be negative

What it means

Returned by convertRestartPolicy (service.go:497-499) when parsing the legacy 'restart' string (e.g. 'unless-stopped:3') and the parsed MaximumRetryCount is negative. The legacy format is 'name[:count]'; a negative count is invalid because retries cannot be negative. Note this path only runs for the legacy service.restart field - service.deploy.restart_policy is handled earlier (lines 479-486) and bypasses this check.

Solutions

  1. Set a non-negative retry count: restart: "on-failure:3".
  2. For unlimited retries use a high positive number or 'always'/'unless-stopped' (which ignore the count).
  3. Prefer the modern deploy.restart_policy block (max_attempts) over the legacy string where possible.

Example fix

# before
services:
  web:
    restart: "on-failure:-5"

# after
services:
  web:
    restart: "on-failure:5"
Defensive patterns

Strategy: validation

Validate before calling

// Validate a legacy 'restart' string before handing it to conversion.
func validateLegacyRestart(restart string) error {
    if restart == "" {
        return nil
    }
    p, err := opts.ParseRestartPolicy(restart)
    if err != nil {
        return err
    }
    if p.MaximumRetryCount < 0 {
        return errors.New("restart: maximum retry count cannot be negative")
    }
    return nil
}

Type guard

// legacyRestartIsNonNegative reports whether the parsed retry count is valid.
func legacyRestartIsNonNegative(restart string) bool {
    p, err := opts.ParseRestartPolicy(restart)
    if err != nil {
        return false
    }
    return p.MaximumRetryCount >= 0
}

Prevention

When it happens

Trigger: In docker-compose.yml: restart: "on-failure:-5" or any 'name:<negative>' value. opts.ParseRestartPolicy (service.go:493) parses the count; if it is < 0 the guard at line 497 fires.

Common situations: Typo or sign error in the retry count. Templating that computes a count which can go negative under some inputs. Misunderstanding the legacy syntax and writing 'on-failure:-1' intending 'infinite'.

Related errors


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

Appendix: source

Thrown at cli/compose/convert/service.go:498

		// TODO: log or error if both "service.restart" and "service.deploy.restartpolicy" are set.
		return &swarm.RestartPolicy{
			Condition:   swarm.RestartPolicyCondition(restartPolicy.Condition),
			Delay:       composetypes.ConvertDurationPtr(restartPolicy.Delay),
			MaxAttempts: restartPolicy.MaxAttempts,
			Window:      composetypes.ConvertDurationPtr(restartPolicy.Window),
		}, nil
	}
	if restart == "" {
		return nil, nil
	}

	// Fall back to the legacy service.restart restart-policy.
	policy, err := opts.ParseRestartPolicy(restart)
	if err != nil {
		return nil, err
	}
	if policy.MaximumRetryCount < 0 {
		return nil, errors.New("invalid restart policy: maximum retry count cannot be negative")
	}
	uint64Ptr := func(i int) *uint64 {
		if i <= 0 {
			return nil
		}
		p := uint64(i)
		return &p
	}

	switch policy.Name {
	case container.RestartPolicyDisabled, "":
		return nil, nil
	case container.RestartPolicyAlways, container.RestartPolicyUnlessStopped:
		return &swarm.RestartPolicy{
			Condition:   swarm.RestartPolicyConditionAny,
			MaxAttempts: uint64Ptr(policy.MaximumRetryCount),
		}, nil
	case container.RestartPolicyOnFailure:

View on GitHub (pinned to 4f84911bfe)