{"record":{"id":"801fbe77813f6c65","repo":"FuelLabs/fuel-core","slug":"timestamp-not-accessible","errorCode":null,"errorMessage":"Timestamp not accessible","messagePattern":"Timestamp not accessible","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/compression/src/decompress.rs","lineNumber":181,"sourceCode":"}\n\nmacro_rules! decompress_impl {\n    ($($type:ty),*) => { paste::paste! {\n        $(\n            impl<D> DecompressibleBy<DecompressCtx<D>> for $type\n            where\n                D: TemporalRegistry<$type>\n            {\n                async fn decompress_with(\n                    key: RegistryKey,\n                    ctx: &DecompressCtx<D>,\n                ) -> anyhow::Result<Self> {\n                    if key == RegistryKey::DEFAULT_VALUE {\n                        return Ok(<$type>::default());\n                    }\n                    let key_timestamp = ctx.db.read_timestamp(&key)?;\n                    if !ctx.config.is_timestamp_accessible(ctx.timestamp, key_timestamp)? {\n                        anyhow::bail!(\"Timestamp not accessible\");\n                    }\n                    ctx.db.read_registry(&key)\n                }\n            }\n        )*\n    }};\n}\n\ndecompress_impl!(AssetId, ContractId, Address, PredicateCode, ScriptCode);\n\nimpl<D, Specification> DecompressibleBy<DecompressCtx<D>> for Coin<Specification>\nwhere\n    D: DecompressDb,\n    Specification: CoinSpecification,\n    Specification::Predicate: DecompressibleBy<DecompressCtx<D>>,\n    Specification::PredicateData: DecompressibleBy<DecompressCtx<D>>,\n    Specification::PredicateGasUsed: DecompressibleBy<DecompressCtx<D>>,\n    Specification::Witness: DecompressibleBy<DecompressCtx<D>>,","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/FuelLabs/fuel-core/blob/b9d4d170da3a31c9ace5f963d633b326348e0d42/crates/compression/src/decompress.rs#L163-L199","documentation":"The compression crate stores repeated values (AssetId, ContractId, Address, PredicateCode, ScriptCode) in a temporal registry keyed by RegistryKey with a registration timestamp. During decompression of a value for an older block, decompress_with checks ctx.config.is_timestamp_accessible(ctx.timestamp, key_timestamp): if more time has passed since the key was registered than Config::temporal_registry_retention, the entry is considered stale and must not be used, so decompression bails. The registry is intentionally write-once/expiring; old keys are expected to be garbage-collected.","triggerScenarios":"Decompressing a RegistryKey whose registration timestamp is older than temporal_registry_retention relative to the block timestamp being processed (e.g., serving or replaying compressed historical blocks after a long gap, or with a short retention configured).","commonSituations":"temporal_registry_retention tuned down between node restarts; long node downtime followed by replay/backfill of old compressed blocks; querying historical compressed state whose registry entries have expired; clock/Tai64 timestamp anomalies making duration_since compute a too-large value.","solutions":["Only decompress blocks whose timestamp is within temporal_registry_retention of when the values were registered; obtain the data from a fresher source instead.","Increase temporal_registry_retention in the compression Config before registering/compressing, then re-sync so entries live long enough for your historical access pattern.","Re-register (re-compress) the value so a fresh RegistryKey/timestamp is produced, then decompress using the new key.","Verify the block timestamps (Tai64) are sane and ordered; an inverted timestamp produces a related 'Invalid timestamp ordering' error from the same check."],"exampleFix":"// before\nlet registry_retention = Duration::from_secs(60 * 60); // 1h, old blocks fail to decompress\n\n// after\n// keep registry entries valid long enough for your historical block access\nlet registry_retention = Duration::from_secs(60 * 60 * 24 * 30); // 30d\nlet config = fuel_core_compression::config::Config {\n    temporal_registry_retention: registry_retention,\n};","handlingStrategy":"validation","validationCode":"// Before decompressing historical data, check the value is still inside the retention window.\nlet age = Tai64N(block_timestamp, 0)\n    .duration_since(&Tai64N(key_timestamp, 0))\n    .ok_or_else(|| anyhow::anyhow!(\"key timestamp after block timestamp\"))?;\nif age > compression_config.temporal_registry_retention {\n    anyhow::bail!(\"registry key expired; re-register the value instead of decompressing\");\n}\nlet value = ctx.db.read_registry(&key)?;","typeGuard":null,"tryCatchPattern":"// Rust: treat as a non-retryable data-availability failure.\nmatch key.decompress_with(key, &ctx).await {\n    Ok(v) => Ok(v),\n    Err(e) if e.to_string().contains(\"Timestamp not accessible\") => {\n        // value expired from the temporal registry: re-register it, do not retry blindly\n        re_register_and_retry(key, ctx).await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Size temporal_registry_retention to exceed the oldest block age you will ever decompress.","Never decrease temporal_registry_retention on an existing database.","Track registration timestamps alongside RegistryKeys so staleness is checkable before decompression."],"tags":["compression","temporal-registry","retention","rust","decompression"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}