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

  1. Pass a sane expiry (or `None`/no-expiry if supported) instead of a huge seconds value.
  2. Clamp/validate `expiry_secs` at the API boundary to a maximum like 100 years before calling token creation.
  3. Replace the `expect` with explicit overflow handling (skip expiry or return an argument error) if you control the code.
  4. 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

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


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)