{"record":{"id":"e9135a5775ad2d8d","repo":"router-for-me/CLIProxyAPI","slug":"lifecycle-configuration-revision-must-be-positive","errorCode":null,"errorMessage":"lifecycle configuration revision must be positive when present","messagePattern":"lifecycle configuration revision must be positive when present","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/credential_concurrency.go","lineNumber":144,"sourceCode":"\tif !c.releaseMaxBackoffPresent && c.ReleaseMaxBackoff == 0 {\n\t\tc.ReleaseMaxBackoff = defaultReleaseMaxBackoff\n\t}\n\tif !c.busyRetryMinPresent && c.BusyRetryMin == 0 {\n\t\tc.BusyRetryMin = defaultBusyRetryMin\n\t}\n\tif !c.busyRetryMaxPresent && c.BusyRetryMax == 0 {\n\t\tc.BusyRetryMax = defaultBusyRetryMax\n\t}\n\tif !c.maxLimitPresent && c.MaxLimit == 0 {\n\t\tc.MaxLimit = maxCredentialConcurrencyLimit\n\t}\n\treturn c\n}\n\n// ValidateCredentialConcurrency validates values intrinsic to a credential concurrency configuration.\nfunc ValidateCredentialConcurrency(cfg CredentialConcurrencyConfig) error {\n\tif cfg.LifecycleConfigRevision < 0 || (cfg.lifecycleConfigRevisionPresent && cfg.LifecycleConfigRevision == 0) {\n\t\treturn fmt.Errorf(\"lifecycle configuration revision must be positive when present\")\n\t}\n\tif cfg.ObservationBarrierRevision < 0 {\n\t\treturn fmt.Errorf(\"observation barrier revision must not be negative\")\n\t}\n\tif cfg.CPAHeartbeatTimeout <= 0 || cfg.CPACancelBound <= 0 || cfg.ReclaimGrace <= 0 || cfg.CleanupInterval <= 0 {\n\t\treturn fmt.Errorf(\"credential concurrency lifecycle durations must be positive\")\n\t}\n\tif cfg.ReleaseFlushInterval <= 0 || cfg.ReleaseMaxBackoff <= 0 || cfg.BusyRetryMin <= 0 || cfg.BusyRetryMax <= 0 {\n\t\treturn fmt.Errorf(\"credential concurrency limiter durations must be positive\")\n\t}\n\tif cfg.ReleaseMaxBackoff < cfg.ReleaseFlushInterval {\n\t\treturn fmt.Errorf(\"credential concurrency release max backoff must not be less than release flush interval\")\n\t}\n\tif cfg.BusyRetryMin%time.Millisecond != 0 || cfg.BusyRetryMax%time.Millisecond != 0 {\n\t\treturn fmt.Errorf(\"credential concurrency busy retry durations must be whole milliseconds\")\n\t}\n\tif cfg.BusyRetryMax < cfg.BusyRetryMin {\n\t\treturn fmt.Errorf(\"credential concurrency busy retry max must not be less than busy retry min\")","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/config/credential_concurrency.go#L126-L162","documentation":"ValidateCredentialConcurrency rejects the 'credential-concurrency.lifecycle-config-revision' value when it is negative, or when the key was explicitly present in config.yaml but set to 0. Revision 0 is reserved as 'unset'; an explicitly-present revision must be a positive integer because it drives coordinated config rollover between the node and CPA (the usage-keeper side).","triggerScenarios":"config.yaml containing 'credential-concurrency:\\n  lifecycle-config-revision: 0' (explicitly present and zero) or a negative value like -1. Absence of the key is fine (defaults apply); explicit zero is not.","commonSituations":"Copying a test payload that used revision 1 and editing it down to 0 to 'reset' it; templating configs that default numeric fields to 0; misunderstanding revision semantics as a counter you can zero out.","solutions":["Set lifecycle-config-revision to a positive integer, e.g. 1, or delete the line entirely to use defaults.","When rolling out new lifecycle timing values, increment the revision (1, 2, 3...) instead of resetting to 0.","Never use negative values; they fail the same check.","After editing, restart or hot-reload and check startup logs for validation success."],"exampleFix":"# before (config.yaml)\ncredential-concurrency:\n  lifecycle-config-revision: 0\n\n# after\ncredential-concurrency:\n  lifecycle-config-revision: 1","handlingStrategy":"validation","validationCode":"// Go: check revision semantics before applying a config payload.\nfunc lifecycleRevisionOK(explicitPresent bool, rev int64) bool {\n    if rev < 0 {\n        return false\n    }\n    if explicitPresent && rev == 0 {\n        return false\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat lifecycle-config-revision as monotonically increasing; start at 1 and increment.","Delete the key rather than setting 0 when you want defaults.","In templates, emit the key only when a real revision is known."],"tags":["config","validation","credential-concurrency","revision"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}