{"record":{"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/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/compose/convert/service.go#L480-L516","documentation":"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.","triggerScenarios":"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.","commonSituations":"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'.","solutions":["Set a non-negative retry count: restart: \"on-failure:3\".","For unlimited retries use a high positive number or 'always'/'unless-stopped' (which ignore the count).","Prefer the modern deploy.restart_policy block (max_attempts) over the legacy string where possible."],"exampleFix":"# before\nservices:\n  web:\n    restart: \"on-failure:-5\"\n\n# after\nservices:\n  web:\n    restart: \"on-failure:5\"","handlingStrategy":"validation","validationCode":"// Validate a legacy 'restart' string before handing it to conversion.\nfunc validateLegacyRestart(restart string) error {\n    if restart == \"\" {\n        return nil\n    }\n    p, err := opts.ParseRestartPolicy(restart)\n    if err != nil {\n        return err\n    }\n    if p.MaximumRetryCount < 0 {\n        return errors.New(\"restart: maximum retry count cannot be negative\")\n    }\n    return nil\n}","typeGuard":"// legacyRestartIsNonNegative reports whether the parsed retry count is valid.\nfunc legacyRestartIsNonNegative(restart string) bool {\n    p, err := opts.ParseRestartPolicy(restart)\n    if err != nil {\n        return false\n    }\n    return p.MaximumRetryCount >= 0\n}","tryCatchPattern":null,"preventionTips":["Prefer deploy.restart_policy.max_attempts over the legacy 'restart: name:count' string.","If templating a retry count, clamp it to >= 0 before emitting the string.","Run 'docker compose config' to validate before deploy."],"tags":["docker-compose","restart-policy","validation","convert"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}