vectordotdev/vector · error
shutdown_complete_tripwire for source
Error message
shutdown_complete_tripwire for source "{id}" not found in the ShutdownCoordinator What it means
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.
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.
Example fix
// before
Utc.timestamp_millis_opt(i64::from_str(sent_time_str).ok()?)
.single()
.expect("invalid timestamp")
// after
Utc.timestamp_millis_opt(i64::from_str(sent_time_str).ok()?)
.single() Defensive patterns
Strategy: validation
Validate before calling
fn valid_sent_timestamp(ms: i64) -> bool {
(0..=253_402_300_799_000).contains(&ms)
} Type guard
fn parse_sent_ts(s: &str) -> Option<DateTime<Utc>> {
let ms = i64::from_str(s).ok()?;
if !(0..=253_402_300_799_000).contains(&ms) { return None; }
Utc.timestamp_millis_opt(ms).single()
} Try / catch
// The function already returns Option — propagate instead of panicking: Utc.timestamp_millis_opt(i64::from_str(sent_time_str).ok()?).single()
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- backoff never ends
- Pagefind is unavailable.
- Serializer does not support JSON
- shutdown_begun_trigger for source
- shutdown_force_trigger for source
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/4e4a5e01db44ef8d.
Report an issue: GitHub.
Appendix: source
Thrown at lib/vector-common/src/shutdown.rs:275
/// has been taken over with `Self::takeover_source`).
pub fn shutdown_source(
&mut self,
id: &ComponentKey,
deadline: Instant,
) -> impl Future<Output = bool> + use<> {
let (_, begin_shutdown_trigger) = self.begun_triggers.remove(id).unwrap_or_else(|| {
panic!(
"shutdown_begun_trigger for source \"{id}\" not found in the ShutdownCoordinator"
)
});
// This is what actually triggers the source to begin shutting down.
begin_shutdown_trigger.cancel();
let shutdown_complete_tripwire = self
.complete_tripwires
.remove(id)
.unwrap_or_else(|| {
panic!(
"shutdown_complete_tripwire for source \"{id}\" not found in the ShutdownCoordinator"
)
});
let shutdown_force_trigger = self.force_triggers.remove(id).unwrap_or_else(|| {
panic!(
"shutdown_force_trigger for source \"{id}\" not found in the ShutdownCoordinator"
)
});
SourceShutdownCoordinator::shutdown_source_complete(
shutdown_complete_tripwire,
shutdown_force_trigger,
id.clone(),
Some(deadline),
)
}
/// Returned future will finish once all *current* sources have finished.
#[must_use]View on GitHub (pinned to bdb87aeaa4)