{"record":{"id":"8b4066994bad00cf","repo":"neondatabase/neon","slug":"can-t-convert-time-last-modified-e","errorCode":null,"errorMessage":"can't convert time '{last_modified}': {e}","messagePattern":"can't convert time '(.+?)': (.+?)","errorType":"exception","errorClass":"DownloadError","httpStatus":null,"severity":"error","filePath":"libs/remote_storage/src/s3_bucket.rs","lineNumber":829,"sourceCode":"                    AttemptOutcome::Err,\n                    started_at,\n                );\n\n                return Err(DownloadError::Other(\n                    anyhow::Error::new(e).context(\"s3 head object\"),\n                ));\n            }\n        };\n\n        let (Some(last_modified), Some(size)) = (data.last_modified, data.content_length) else {\n            return Err(DownloadError::Other(anyhow!(\n                \"head_object doesn't contain last_modified or content_length\"\n            )))?;\n        };\n        Ok(ListingObject {\n            key: key.to_owned(),\n            last_modified: SystemTime::try_from(last_modified).map_err(|e| {\n                DownloadError::Other(anyhow!(\"can't convert time '{last_modified}': {e}\"))\n            })?,\n            size: size as u64,\n        })\n    }\n\n    async fn upload(\n        &self,\n        from: impl Stream<Item = std::io::Result<Bytes>> + Send + Sync + 'static,\n        from_size_bytes: usize,\n        to: &RemotePath,\n        metadata: Option<StorageMetadata>,\n        cancel: &CancellationToken,\n    ) -> anyhow::Result<()> {\n        let kind = RequestKind::Put;\n        let _permit = self.permit(kind, cancel).await?;\n\n        let started_at = start_measuring_requests(kind);\n","sourceCodeStart":811,"sourceCodeEnd":847,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/remote_storage/src/s3_bucket.rs#L811-L847","documentation":"After a successful HeadObject, last_modified (an aws_smithy_types::DateTime) is converted with SystemTime::try_from; the conversion fails when the timestamp is outside SystemTime's range, most commonly a date before the Unix epoch. The offending timestamp is embedded in the message, so you can see exactly what the endpoint reported.","triggerScenarios":"An S3-compatible endpoint reporting a zero, negative, or otherwise out-of-range Last-Modified for a key being listed, e.g. an emulated default of 1970-01-01 minus an offset, or a gateway with broken clock handling.","commonSituations":"Emulators defaulting timestamps incorrectly; objects written by tooling with timezone math bugs; systems with wrong RTC clocks.","solutions":["Compare the timestamp in the error message with aws s3api head-object for the same key","Fix the emulator/uploader so Last-Modified is a valid post-epoch RFC 3339/HTTP date","Overwrite or delete the malformed object if the endpoint cannot be fixed"],"exampleFix":"// before: emulator writes epoch-relative stamps\nresponse.insert_header(\"Last-Modified\", \"Wed, 31 Dec 1969 23:59:59 GMT\");\n// after: emit a real UTC time\nresponse.insert_header(\"Last-Modified\", httpdate::fmt_http_date(std::time::SystemTime::now()));","handlingStrategy":"try-catch","validationCode":"use aws_sdk_s3::Client;\n\nasync fn last_modified_in_range(client: &Client, bucket: &str, key: &str) -> anyhow::Result<bool> {\n    let head = client.head_object().bucket(bucket).key(key).send().await?;\n    Ok(head.last_modified\n        .map(|t| t.secs() >= 0)\n        .unwrap_or(false))\n}","typeGuard":"fn is_time_conversion_error(err: &remote_storage::DownloadError) -> bool {\n    matches!(err, remote_storage::DownloadError::Other(e)\n        if e.to_string().contains(\"can't convert time\"))\n}","tryCatchPattern":"match storage.list_files(&prefix, mode, &cancel).await {\n    Err(DownloadError::Other(e)) if e.to_string().contains(\"can't convert time\") => {\n        // the message embeds the offending timestamp; fix or remove that object/endpoint\n    }\n    other => other?,\n}","preventionTips":["Keep NTP/RTC correct on hosts that write objects","Assert emulators emit post-epoch Last-Modified in their test suites","Log the key along with the error so the malformed object can be located"],"tags":["s3","rust","timestamp","remote-storage"],"backgroundTag":"invalid-timestamp","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}