router-for-me/CLIProxyAPI · error

lifecycle configuration revision must be positive when prese

Error message

lifecycle configuration revision must be positive when present

What it means

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).

Source

Thrown at internal/config/credential_concurrency.go:144

	if !c.releaseMaxBackoffPresent && c.ReleaseMaxBackoff == 0 {
		c.ReleaseMaxBackoff = defaultReleaseMaxBackoff
	}
	if !c.busyRetryMinPresent && c.BusyRetryMin == 0 {
		c.BusyRetryMin = defaultBusyRetryMin
	}
	if !c.busyRetryMaxPresent && c.BusyRetryMax == 0 {
		c.BusyRetryMax = defaultBusyRetryMax
	}
	if !c.maxLimitPresent && c.MaxLimit == 0 {
		c.MaxLimit = maxCredentialConcurrencyLimit
	}
	return c
}

// ValidateCredentialConcurrency validates values intrinsic to a credential concurrency configuration.
func ValidateCredentialConcurrency(cfg CredentialConcurrencyConfig) error {
	if cfg.LifecycleConfigRevision < 0 || (cfg.lifecycleConfigRevisionPresent && cfg.LifecycleConfigRevision == 0) {
		return fmt.Errorf("lifecycle configuration revision must be positive when present")
	}
	if cfg.ObservationBarrierRevision < 0 {
		return fmt.Errorf("observation barrier revision must not be negative")
	}
	if cfg.CPAHeartbeatTimeout <= 0 || cfg.CPACancelBound <= 0 || cfg.ReclaimGrace <= 0 || cfg.CleanupInterval <= 0 {
		return fmt.Errorf("credential concurrency lifecycle durations must be positive")
	}
	if cfg.ReleaseFlushInterval <= 0 || cfg.ReleaseMaxBackoff <= 0 || cfg.BusyRetryMin <= 0 || cfg.BusyRetryMax <= 0 {
		return fmt.Errorf("credential concurrency limiter durations must be positive")
	}
	if cfg.ReleaseMaxBackoff < cfg.ReleaseFlushInterval {
		return fmt.Errorf("credential concurrency release max backoff must not be less than release flush interval")
	}
	if cfg.BusyRetryMin%time.Millisecond != 0 || cfg.BusyRetryMax%time.Millisecond != 0 {
		return fmt.Errorf("credential concurrency busy retry durations must be whole milliseconds")
	}
	if cfg.BusyRetryMax < cfg.BusyRetryMin {
		return fmt.Errorf("credential concurrency busy retry max must not be less than busy retry min")

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Set lifecycle-config-revision to a positive integer, e.g. 1, or delete the line entirely to use defaults.
  2. When rolling out new lifecycle timing values, increment the revision (1, 2, 3...) instead of resetting to 0.
  3. Never use negative values; they fail the same check.
  4. After editing, restart or hot-reload and check startup logs for validation success.

Example fix

# before (config.yaml)
credential-concurrency:
  lifecycle-config-revision: 0

# after
credential-concurrency:
  lifecycle-config-revision: 1
Defensive patterns

Strategy: validation

Validate before calling

// Go: check revision semantics before applying a config payload.
func lifecycleRevisionOK(explicitPresent bool, rev int64) bool {
    if rev < 0 {
        return false
    }
    if explicitPresent && rev == 0 {
        return false
    }
    return true
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/e9135a5775ad2d8d. Report an issue: GitHub.