{"record":{"id":"4e4a5e01db44ef8d","repo":"vectordotdev/vector","slug":"shutdown-complete-tripwire-for-source-id-not-found-in-the","errorCode":null,"errorMessage":"shutdown_complete_tripwire for source \"{id}\" not found in the ShutdownCoordinator","messagePattern":"shutdown_complete_tripwire for source \"(.+?)\" not found in the ShutdownCoordinator","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/vector-common/src/shutdown.rs","lineNumber":275,"sourceCode":"    /// has been taken over with `Self::takeover_source`).\n    pub fn shutdown_source(\n        &mut self,\n        id: &ComponentKey,\n        deadline: Instant,\n    ) -> impl Future<Output = bool> + use<> {\n        let (_, begin_shutdown_trigger) = self.begun_triggers.remove(id).unwrap_or_else(|| {\n            panic!(\n                \"shutdown_begun_trigger for source \\\"{id}\\\" not found in the ShutdownCoordinator\"\n            )\n        });\n        // This is what actually triggers the source to begin shutting down.\n        begin_shutdown_trigger.cancel();\n\n        let shutdown_complete_tripwire = self\n            .complete_tripwires\n            .remove(id)\n            .unwrap_or_else(|| {\n                panic!(\n                \"shutdown_complete_tripwire for source \\\"{id}\\\" not found in the ShutdownCoordinator\"\n            )\n            });\n        let shutdown_force_trigger = self.force_triggers.remove(id).unwrap_or_else(|| {\n            panic!(\n                \"shutdown_force_trigger for source \\\"{id}\\\" not found in the ShutdownCoordinator\"\n            )\n        });\n        SourceShutdownCoordinator::shutdown_source_complete(\n            shutdown_complete_tripwire,\n            shutdown_force_trigger,\n            id.clone(),\n            Some(deadline),\n        )\n    }\n\n    /// Returned future will finish once all *current* sources have finished.\n    #[must_use]","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/lib/vector-common/src/shutdown.rs#L257-L293","documentation":"Panic from `.expect(\"invalid timestamp\")` in `get_timestamp` (src/sources/aws_sqs/source.rs:192), converting the SQS `SentTimestamp` message-system attribute (milliseconds since epoch, as a string) into a chrono `DateTime<Utc>` via `Utc.timestamp_millis_opt`. `timestamp_millis_opt` returns None when the millisecond value is outside chrono's supported range, so the expect fires on an out-of-range or absurd SentTimestamp.","triggerScenarios":"The SQS message attribute `SentTimestamp` parses to an i64 (e.g. `0`, negative, or an astronomically large value) that `Utc.timestamp_millis_opt` cannot convert to a valid UTC datetime; also triggered by i64 overflow via `i64::from_str(sent_time_str)` on a 19+ digit string that fits i64 but exceeds chrono's date range.","commonSituations":"Test harnesses or mock SQS producers setting SentTimestamp to 0 or placeholder values; a producer bug emitting epoch-nanoseconds or epoch-microseconds instead of milliseconds; clock skew or corrupted queue attributes.","solutions":["Validate the parsed millis against a plausible range (e.g. 0..=253_402_300_799_000) and return None (the function already returns Option) instead of panicking.","Replace expect with `.single()?` so an invalid timestamp simply omits the timestamp field.","Check the producer of the messages is sending milliseconds-since-epoch, not another unit."],"exampleFix":"// before\nUtc.timestamp_millis_opt(i64::from_str(sent_time_str).ok()?)\n    .single()\n    .expect(\"invalid timestamp\")\n// after\nUtc.timestamp_millis_opt(i64::from_str(sent_time_str).ok()?)\n    .single()","handlingStrategy":"validation","validationCode":"fn valid_sent_timestamp(ms: i64) -> bool {\n    (0..=253_402_300_799_000).contains(&ms)\n}","typeGuard":"fn parse_sent_ts(s: &str) -> Option<DateTime<Utc>> {\n    let ms = i64::from_str(s).ok()?;\n    if !(0..=253_402_300_799_000).contains(&ms) { return None; }\n    Utc.timestamp_millis_opt(ms).single()\n}","tryCatchPattern":"// The function already returns Option — propagate instead of panicking:\nUtc.timestamp_millis_opt(i64::from_str(sent_time_str).ok()?).single()","preventionTips":["Bound-check epoch millis before chrono conversion; reject 0/placeholder and > year-9999 values.","Confirm producers send milliseconds (SQS contract), not seconds/micro/nanos.","Keep get_timestamp total (Option-returning) and add unit tests for 0 and i64 extremes."],"tags":["aws","sqs","timestamp","chrono","rust","panic"],"backgroundTag":"value-out-of-range","analyzedSha":"bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013","analyzedAt":"2026-09-16T02:53:35.741Z","contentChangedAt":"2026-09-16T02:53:35.741Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}