zeroclaw-labs/zeroclaw · error
security.otp.cache_valid_secs must be greater than or equal
Error message
security.otp.cache_valid_secs must be greater than or equal to security.otp.token_ttl_secs
What it means
ZeroClaw requires security.otp.cache_valid_secs to be greater than or equal to security.otp.token_ttl_secs so the OTP cache always outlives the tokens it caches — a shorter cache window would still be legal, but this invariant is enforced to keep cached entries valid for the token's whole lifetime. The comparison runs after the separate `cache_valid_secs > 0` guard, so a zero value fails earlier with a different message. Equality is allowed (>=).
Source
Thrown at crates/zeroclaw-config/src/schema.rs:21396
"security.otp.challenge_max_attempts must be greater than 0"
);
}
if self.security.otp.token_ttl_secs == 0 {
validation_bail!(
InvalidNumericRange,
"security.otp.token_ttl_secs",
"security.otp.token_ttl_secs must be greater than 0"
);
}
if self.security.otp.cache_valid_secs == 0 {
validation_bail!(
InvalidNumericRange,
"security.otp.cache_valid_secs",
"security.otp.cache_valid_secs must be greater than 0"
);
}
if self.security.otp.cache_valid_secs < self.security.otp.token_ttl_secs {
anyhow::bail!(
"security.otp.cache_valid_secs must be greater than or equal to security.otp.token_ttl_secs"
);
}
if self.security.otp.challenge_max_attempts == 0 {
validation_bail!(
InvalidNumericRange,
"security.otp.challenge_max_attempts",
"security.otp.challenge_max_attempts must be greater than 0"
);
}
for (i, action) in self.security.otp.gated_actions.iter().enumerate() {
let normalized = action.trim();
if normalized.is_empty() {
validation_bail!(
RequiredFieldEmpty,
format!("security.otp.gated_actions[{i}]"),
"security.otp.gated_actions[{i}] must not be empty"
);View on GitHub (pinned to 88bb9c8533)
Solutions
- Raise cache_valid_secs to at least token_ttl_secs (e.g. cache_valid_secs = 300, token_ttl_secs = 300)
- Or lower token_ttl_secs below the cache window if shorter-lived tokens are acceptable
- Keep both keys adjacent in the [security.otp] block so they are edited together
- Remember cache_valid_secs must also be > 0 — zero fails an earlier check with its own message
Example fix
# before [security.otp] cache_valid_secs = 60 token_ttl_secs = 300 # after [security.otp] cache_valid_secs = 300 token_ttl_secs = 300
Defensive patterns
Strategy: validation
Validate before calling
fn otp_ttl_precheck(otp: &zeroclaw_config::OtpConfig) -> Result<(), String> {
if otp.cache_valid_secs == 0 {
return Err("security.otp.cache_valid_secs must be greater than 0".into());
}
if otp.cache_valid_secs < otp.token_ttl_secs {
return Err("cache_valid_secs must be >= token_ttl_secs".into());
}
Ok(())
} Type guard
fn otp_windows_consistent(otp: &zeroclaw_config::OtpConfig) -> bool {
otp.cache_valid_secs > 0 && otp.cache_valid_secs >= otp.token_ttl_secs
} Try / catch
if let Err(err) = config.validate() {
if err.to_string().contains("cache_valid_secs must be greater than or equal") {
// bump cache_valid_secs to token_ttl_secs and re-validate
}
} Prevention
- Derive cache_valid_secs from token_ttl_secs in config templates (e.g. ttl * 5) instead of hardcoding both
- Document the invariant next to the keys in shared configs
- Add a unit test that asserts the invariant for every shipped profile
When it happens
Trigger: Configure e.g. `token_ttl_secs = 300` with `cache_valid_secs = 60`. Any combination where the cache window is strictly smaller than the token TTL bails during security validation.
Common situations: Hardening passes that shrink cache windows to limit replay risk; adopting a 30s TOTP step for token_ttl while leaving a stale small cache value; env-mirror overrides that set only one of the two keys.
Related errors
- security.otp.gated_actions[{i}] contains invalid characters:
- cloud_ops.iac_tools must not be empty when cloud_ops is enab
- gateway.path_prefix contains invalid character '{bad}'; only
- risk_profiles.{profile_alias}.shell_env_passthrough[{i}] is
- providers.models.{profile_name}.uri must use http/https
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/ef3b40269446c69a.
Report an issue: GitHub.