{"record":{"id":"c3f6cfd0dbd32659","repo":"vectordotdev/vector","slug":"invalid-timestamp-c3f6cf","errorCode":null,"errorMessage":"invalid timestamp","messagePattern":"invalid timestamp","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/sources/fluent/message.rs","lineNumber":122,"sourceCode":"                    .next_element()?\n                    .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;\n\n                if bytes.len() != 8 {\n                    return Err(serde::de::Error::custom(format!(\n                        \"expected exactly 8 bytes for binary encoded fluent timestamp, got {}\",\n                        bytes.len()\n                    )));\n                }\n\n                // length checked right above\n                let seconds = u32::from_be_bytes(bytes[..4].try_into().expect(\"exactly 4 bytes\"));\n                let nanoseconds =\n                    u32::from_be_bytes(bytes[4..].try_into().expect(\"exactly 4 bytes\"));\n\n                Ok(FluentEventTime(\n                    Utc.timestamp_opt(seconds.into(), nanoseconds)\n                        .single()\n                        .expect(\"invalid timestamp\"),\n                ))\n            }\n        }\n\n        deserializer.deserialize_any(FluentEventTimeVisitor)\n    }\n}\n\n/// Value for fluent record key.\n///\n/// Used mostly just to implement value conversion.\n#[derive(Debug, Deserialize, PartialEq, Serialize)]\npub(super) struct FluentValue(rmpv::Value);\n\nimpl From<rmpv::Value> for FluentValue {\n    fn from(value: rmpv::Value) -> Self {\n        Self(value)\n    }","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/vectordotdev/vector/blob/3708c39b12a93212ed8b8d7510b4cc7769cb5864/src/sources/fluent/message.rs#L104-L140","documentation":"The fluent source decodes MessagePack bodies in the fluent forward protocol. Fluent's EventTime is an 8-byte extension (ext type 0): 4 bytes big-endian Unix seconds plus 4 bytes nanoseconds. After the length check, Vector converts with Utc.timestamp_opt(seconds, nanoseconds).single().expect(\"invalid timestamp\"). chrono rejects nanoseconds >= 2,000,000,000 and seconds outside its roughly +-262k-year window by returning None, so a well-sized but out-of-range extension panics the decoding task.","triggerScenarios":"A fluentd/fluent-bit peer (or any forward-protocol client) sending EventTime whose nanosecond word is >= 2e9 - for example milliseconds or microseconds written into the nanos field - or garbage seconds. The 8-byte length check passes, then chrono refuses the value.","commonSituations":"Interop with non-compliant forwarders, corrupted TCP frames, replayed or fuzzed captures against the fluent endpoint. One malformed timestamp in a MessagePack array kills the source task and the pipeline with it.","solutions":["Fix or sanitize the sender so EventTime nanoseconds stay below 1,000,000,000 and seconds are sane","Patch the visitor to return a serde error instead of expecting, so one bad event fails that message's decode rather than panicking","Restrict which hosts can reach the fluent TCP port","Upgrade Vector once the fix lands"],"exampleFix":"// before\nOk(FluentEventTime(\n    Utc.timestamp_opt(seconds.into(), nanoseconds)\n        .single()\n        .expect(\"invalid timestamp\"),\n))\n\n// after\nmatch Utc.timestamp_opt(seconds.into(), nanoseconds).single() {\n    Some(ts) => Ok(FluentEventTime(ts)),\n    None => Err(E::custom(format!(\n        \"EventTime out of range: seconds={seconds}, nanoseconds={nanoseconds}\"\n    ))),\n}","handlingStrategy":"validation","validationCode":"fn valid_fluent_event_time(seconds: u32, nanos: u32) -> bool {\n    let secs_ok = (seconds as i64) >= -8_334_601_228_800 && (seconds as i64) <= 8_210_266_876_799;\n    secs_ok && nanos < 2_000_000_000\n}\n\n// in the visitor, before constructing the timestamp:\nif !valid_fluent_event_time(seconds, nanoseconds) {\n    return Err(E::custom(\"EventTime out of range\"));\n}","typeGuard":"fn parse_fluent_event_time(seconds: u32, nanos: u32) -> Option<chrono::DateTime<chrono::Utc>> {\n    Utc.timestamp_opt(seconds as i64, nanoseconds).single()\n}","tryCatchPattern":"// inside Deserialize: return Err instead of expect so serde fails per message\nmatch Utc.timestamp_opt(seconds.into(), nanoseconds).single() {\n    Some(ts) => Ok(FluentEventTime(ts)),\n    None => Err(E::custom(format!(\n        \"EventTime out of range: seconds={seconds}, nanos={nanoseconds}\"\n    ))),\n}","preventionTips":["Never expect() inside Deserialize implementations; external input must produce Err, not panic","Unit-test deserializers with nanos >= 2e9 and extreme seconds","Restrict fluent endpoint exposure to known forwarders","Add fuzzing (cargo-fuzz) for the msgpack decoder"],"tags":["rust","panic","msgpack","fluent","timestamp","deserialization"],"backgroundTag":"timestamp-out-of-range","analyzedSha":"3708c39b12a93212ed8b8d7510b4cc7769cb5864","analyzedAt":"2026-08-20T07:02:18.786Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}