{"record":{"id":"e069dbb65557d136","repo":"router-for-me/CLIProxyAPI","slug":"credential-concurrency-busy-retry-durations-must-b","errorCode":null,"errorMessage":"credential concurrency busy retry durations must be whole milliseconds","messagePattern":"credential concurrency busy retry durations must be whole milliseconds","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/credential_concurrency.go","lineNumber":159,"sourceCode":"// 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\")\n\t}\n\tif cfg.MaxLimit < 1 || cfg.MaxLimit > maxCredentialConcurrencyLimit {\n\t\treturn fmt.Errorf(\"credential concurrency max limit must be between 1 and %d\", maxCredentialConcurrencyLimit)\n\t}\n\treturn nil\n}\n\n// ValidateCredentialConcurrencyLifecycle verifies the Home lifecycle timing safety invariant.\nfunc ValidateCredentialConcurrencyLifecycle(nodeHeartbeatTimeout time.Duration, cfg CredentialConcurrencyConfig) error {\n\tif nodeHeartbeatTimeout <= 0 {\n\t\treturn fmt.Errorf(\"credential concurrency lifecycle durations must be positive\")\n\t}\n\tif errValidate := ValidateCredentialConcurrency(cfg); errValidate != nil {\n\t\treturn errValidate\n\t}","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/config/credential_concurrency.go#L141-L177","documentation":"ValidateCredentialConcurrency requires busy-retry-min and busy-retry-max to be exact whole milliseconds (duration % time.Millisecond == 0). Sub-millisecond values such as 250.5µs or 1ms500µs (1500µs is fine, but 1500ns is not) are rejected because the retry limiter schedules on millisecond granularity.","triggerScenarios":"config.yaml with 'busy-retry-min: 250us', 'busy-retry-max: 1.5ms' expressed via micro/nanosecond units that are not a whole number of milliseconds (e.g. 'busy-retry-min: 1500ns').","commonSituations":"Aggressive latency tuning copied from a benchmark using microsecond values; unit confusion between us/ms; generated configs emitting floats that yaml decodes into sub-millisecond durations.","solutions":["Round busy-retry-min/max to whole milliseconds, e.g. 250ms and 1s.","Avoid microsecond (us/µs) and nanosecond (ns) units for these two keys.","Keep busy-retry-max >= busy-retry-min after rounding."],"exampleFix":"# before (config.yaml)\ncredential-concurrency:\n  busy-retry-min: 250us\n  busy-retry-max: 1s\n\n# after\ncredential-concurrency:\n  busy-retry-min: 250ms\n  busy-retry-max: 1s","handlingStrategy":"validation","validationCode":"// Go: enforce whole-millisecond retries.\nfunc wholeMillis(d time.Duration) bool { return d%time.Millisecond == 0 }\n\nif !wholeMillis(cfg.BusyRetryMin) || !wholeMillis(cfg.BusyRetryMax) {\n    return errors.New(\"busy retry bounds must be whole milliseconds\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use ms/s units only for busy-retry values.","Do not port microsecond-tuned benchmark values into config."],"tags":["config","validation","credential-concurrency","durations","precision"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}