{"record":{"id":"60fa74cdfaa557d7","repo":"influxdata/influxdb","slug":"duration-not-to-overflow","errorCode":null,"errorMessage":"duration not to overflow","messagePattern":"duration not to overflow","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"influxdb3_catalog/src/catalog/versions/v1/enterprise.rs","lineNumber":46,"sourceCode":"        &self,\n        all_permissions: Vec<PermissionDetailsSpec>,\n        token_name: String,\n        expiry_secs: Option<u64>,\n    ) -> Result<(Arc<TokenInfo>, String)> {\n        let (token, hash) = create_token_and_hash();\n        self.catalog_update_with_retry(|| {\n            if self.inner.read().tokens.repo().contains_name(&token_name) {\n                return Err(CatalogError::TokenNameAlreadyExists(token_name.clone()));\n            }\n\n            let (token_id, created_at, expiry) = {\n                let mut inner = self.inner.write();\n                let token_id = inner.tokens.get_and_increment_next_id();\n                let created_at = self.time_provider.now();\n                let expiry = expiry_secs.map(|secs| {\n                    created_at\n                        .checked_add(Duration::from_secs(secs))\n                        .expect(\"duration not to overflow\")\n                        .timestamp_millis()\n                });\n                (token_id, created_at.timestamp_millis(), expiry)\n            };\n\n            // NB: the validation happens here for parsing but the parsed types aren't used in\n            //     `CreateTokenDetails` currently. This will be addressed in\n            //     https://github.com/influxdata/influxdb_pro/issues/745\n            let all_perms = all_permissions.iter().try_fold(\n                Vec::with_capacity(all_permissions.len()),\n                |mut permission_details, api_permission| {\n                    let resource_type = ResourceType::from_str(&api_permission.resource_type)\n                        .map(|res_type| {\n                            if matches!(res_type, ResourceType::Wildcard) {\n                                Err(CatalogError::CannotParsePermissionForToken(\n                                    \"* resource type can only be set for admin token\".to_string(),\n                                ))\n                            } else {","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_catalog/src/catalog/versions/v1/enterprise.rs#L28-L64","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\n.expect(\"duration not to overflow\")\n// after\n.checked_add(Duration::from_secs(secs))\n.ok_or_else(|| anyhow!(\"expiry of {secs}s overflows timestamp\"))?","handlingStrategy":"validation","validationCode":"// cap expiry at 100 years before calling create_token_with_permission\nconst MAX_EXPIRY_SECS: u64 = 100 * 365 * 24 * 60 * 60;\nlet expiry_secs = expiry_secs.map(|s| s.min(MAX_EXPIRY_SECS));","typeGuard":"fn expiry_representable(created_at_ms: i64, secs: u64) -> bool {\n    created_at_ms.checked_add((secs * 1000) as i64).is_some()\n}","tryCatchPattern":"// this is a panic, so guard before the call\nif let Some(s) = expiry_secs {\n    assert!(s <= MAX_EXPIRY_SECS, \"token expiry {s}s too large\");\n}","preventionTips":["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."],"tags":["influxdb3","auth","token","overflow","panic","rust"],"backgroundTag":"value-out-of-range","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}