influxdata/influxdb · error
duration not to overflow
Error message
duration not to overflow
What it means
A `.expect(...)` panic in the enterprise catalog's token creation: the expiry, computed as `created_at + Duration::from_secs(secs)` with `checked_add`, overflows the timestamp. The code assumes caller-provided `expiry_secs` always yields a representable timestamp; a huge expiry value breaks that assumption and panics.
Solutions
- Pass a sane expiry (or `None`/no-expiry if supported) instead of a huge seconds value.
- Clamp/validate `expiry_secs` at the API boundary to a maximum like 100 years before calling token creation.
- Replace the `expect` with explicit overflow handling (skip expiry or return an argument error) if you control the code.
- Store long-lived expiry as a far-future but representable timestamp rather than computing it from a raw seconds offset.
Example fix
// before
.expect("duration not to overflow")
// after
.checked_add(Duration::from_secs(secs))
.ok_or_else(|| anyhow!("expiry of {secs}s overflows timestamp"))? Defensive patterns
Strategy: validation
Validate before calling
// cap expiry at 100 years before calling create_token_with_permission const MAX_EXPIRY_SECS: u64 = 100 * 365 * 24 * 60 * 60; let expiry_secs = expiry_secs.map(|s| s.min(MAX_EXPIRY_SECS));
Type guard
fn expiry_representable(created_at_ms: i64, secs: u64) -> bool {
created_at_ms.checked_add((secs * 1000) as i64).is_some()
} Try / catch
// this is a panic, so guard before the call
if let Some(s) = expiry_secs {
assert!(s <= MAX_EXPIRY_SECS, "token expiry {s}s too large");
} Prevention
- Model 'never expires' as None/null, not as a giant seconds count.
- Clamp TTLs at the API/config boundary to a bounded maximum.
- Add unit tests for token creation with extreme expiry values.
- Never pass u64::MAX or Duration::MAX style constants as TTLs.
When it happens
Trigger: Calling `create_token_with_permission` (enterprise catalog) with `expiry_secs` large enough that `created_at + expiry_secs` overflows the datetime type (e.g. u64::MAX seconds, or years beyond the timestamp's max range).
Common situations: Passing 'never expire' as a giant number of seconds instead of None; config or API payloads with u64::MAX/u32::MAX as TTL; test code reusing a max-value constant for expiry.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- token info must be present after token creation by name
- generation duration overflows u64 nanoseconds
- resource type should be parseable
- row_delete_predicate_version exceeds u64
- cannot fit duration into u64
AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19).
Data as JSON: /api/errors/60fa74cdfaa557d7.
Report an issue: GitHub.
Appendix: source
Thrown at influxdb3_catalog/src/catalog/versions/v1/enterprise.rs:46
&self,
all_permissions: Vec<PermissionDetailsSpec>,
token_name: String,
expiry_secs: Option<u64>,
) -> Result<(Arc<TokenInfo>, String)> {
let (token, hash) = create_token_and_hash();
self.catalog_update_with_retry(|| {
if self.inner.read().tokens.repo().contains_name(&token_name) {
return Err(CatalogError::TokenNameAlreadyExists(token_name.clone()));
}
let (token_id, created_at, expiry) = {
let mut inner = self.inner.write();
let token_id = inner.tokens.get_and_increment_next_id();
let created_at = self.time_provider.now();
let expiry = expiry_secs.map(|secs| {
created_at
.checked_add(Duration::from_secs(secs))
.expect("duration not to overflow")
.timestamp_millis()
});
(token_id, created_at.timestamp_millis(), expiry)
};
// NB: the validation happens here for parsing but the parsed types aren't used in
// `CreateTokenDetails` currently. This will be addressed in
// https://github.com/influxdata/influxdb_pro/issues/745
let all_perms = all_permissions.iter().try_fold(
Vec::with_capacity(all_permissions.len()),
|mut permission_details, api_permission| {
let resource_type = ResourceType::from_str(&api_permission.resource_type)
.map(|res_type| {
if matches!(res_type, ResourceType::Wildcard) {
Err(CatalogError::CannotParsePermissionForToken(
"* resource type can only be set for admin token".to_string(),
))
} else {View on GitHub (pinned to 06200ef96b)