{"id":"b6c478bf9f0c6d1c","repo":"docker/cli","slug":"invalid-restart-policy-maximum-retry-count-cannot","errorCode":null,"errorMessage":"invalid restart policy: maximum retry count cannot be negative","messagePattern":"invalid restart policy: maximum retry count cannot be negative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/compose/convert/service.go","lineNumber":498,"sourceCode":"\t\t// TODO: log or error if both \"service.restart\" and \"service.deploy.restartpolicy\" are set.\n\t\treturn &swarm.RestartPolicy{\n\t\t\tCondition:   swarm.RestartPolicyCondition(restartPolicy.Condition),\n\t\t\tDelay:       composetypes.ConvertDurationPtr(restartPolicy.Delay),\n\t\t\tMaxAttempts: restartPolicy.MaxAttempts,\n\t\t\tWindow:      composetypes.ConvertDurationPtr(restartPolicy.Window),\n\t\t}, nil\n\t}\n\tif restart == \"\" {\n\t\treturn nil, nil\n\t}\n\n\t// Fall back to the legacy service.restart restart-policy.\n\tpolicy, err := opts.ParseRestartPolicy(restart)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif policy.MaximumRetryCount < 0 {\n\t\treturn nil, errors.New(\"invalid restart policy: maximum retry count cannot be negative\")\n\t}\n\tuint64Ptr := func(i int) *uint64 {\n\t\tif i <= 0 {\n\t\t\treturn nil\n\t\t}\n\t\tp := uint64(i)\n\t\treturn &p\n\t}\n\n\tswitch policy.Name {\n\tcase container.RestartPolicyDisabled, \"\":\n\t\treturn nil, nil\n\tcase container.RestartPolicyAlways, container.RestartPolicyUnlessStopped:\n\t\treturn &swarm.RestartPolicy{\n\t\t\tCondition:   swarm.RestartPolicyConditionAny,\n\t\t\tMaxAttempts: uint64Ptr(policy.MaximumRetryCount),\n\t\t}, nil\n\tcase container.RestartPolicyOnFailure:","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/docker/cli/blob/e9452d6e785f6e365712b9d71bd7517591773c86/cli/compose/convert/service.go#L480-L516","documentation":"Raised by convertRestartPolicy when a compose service uses the legacy top-level `restart:` string (not deploy.restart_policy) and parsing it yields a negative maximum retry count. Swarm stores MaxAttempts as an unsigned value, so a negative count is meaningless and rejected during stack conversion.","triggerScenarios":"`docker stack deploy` with a service like `restart: on-failure:-1` (or any on-failure:<negative> form) and no `deploy.restart_policy` overriding it. The legacy string is parsed via opts.ParseRestartPolicy and the MaximumRetryCount < 0 check fails.","commonSituations":"Users writing `on-failure:-1` intending 'retry forever' (the correct way is plain `on-failure` or `restart: always`); generated compose files with templated retry counts that go negative; porting configs from systems where -1 means unlimited.","solutions":["Use `restart: on-failure` without a count for unlimited retries, or a non-negative count like `on-failure:5`","Prefer the swarm-native form: `deploy.restart_policy: {condition: on-failure, max_attempts: 5}`","Validate templated values so retry counts are >= 0 before deploy"],"exampleFix":"# before\nrestart: on-failure:-1\n# after\ndeploy:\n  restart_policy:\n    condition: on-failure\n    max_attempts: 5","handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":["docker-cli","compose","swarm","restart-policy","validation"],"analyzedSha":"e9452d6e785f6e365712b9d71bd7517591773c86","analyzedAt":"2026-08-01T07:09:22.474Z","schemaVersion":2}