vectordotdev/vector · error
unit structs should be rejected during AST parsing
Error message
unit structs should be rejected during AST parsing
What it means
Same panic family as errorIndex 8 but on the rate-metric branch of `decode_ddseries_v2` (src/sources/datadog_agent/metrics.rs:345): `Utc.timestamp_opt(dd_point.timestamp, 0).single().expect("invalid timestamp")` panics when the Datadog point's seconds-since-epoch timestamp is outside chrono's supported range. It affects rate/count-type points (branch with `.with_interval_ms(non_rate_interval)`).
Solutions
- Validate `dd_point.timestamp` against a sane epoch-seconds range before building the metric and drop the point with a warning on failure.
- Use `.single()` result directly (Option) and skip the point instead of expect, mirroring the non-rate branch fix.
- Convert milliseconds to seconds (`ts / 1000`) if the upstream emitter documents ms units.
Example fix
// before
.with_timestamp(Some(
Utc.timestamp_opt(dd_point.timestamp, 0)
.single()
.expect("invalid timestamp"),
))
// after
let ts = Utc.timestamp_opt(dd_point.timestamp, 0).single()?;
// ... .with_timestamp(Some(ts)) Defensive patterns
Strategy: validation
Validate before calling
fn valid_dd_timestamp(secs: i64) -> bool {
(0..=253_402_300_799).contains(&secs)
} Type guard
fn dd_ts_to_utc(secs: i64) -> Option<DateTime<Utc>> {
if !(0..=253_402_300_799).contains(&secs) { return None; }
Utc.timestamp_opt(secs, 0).single()
} Try / catch
// In the rate branch, propagate None upward: let ts = Utc.timestamp_opt(dd_point.timestamp, 0).single()?; // builder.with_timestamp(Some(ts))
Prevention
- Apply the same timestamp guard on every decode branch (gauge and rate), not just one.
- Reject or rescale out-of-range timestamps with a warning metric for observability.
- Share one helper for point-timestamp conversion across all Datadog metric types.
When it happens
Trigger: A v2 series payload with a rate/count point whose `timestamp` i64 cannot be represented as a UTC datetime — epoch-milliseconds supplied where seconds are expected, negative values, or i64-near-max garbage from a malformed/protobuf-unmarshal-adjacent issue.
Common situations: Third-party emitters masquerading as the Datadog agent sending wrong timestamp units; corrupted or truncated protobuf payloads; test fixtures with placeholder timestamps like 0 or i64::MAX.
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
- Tried to clean schema reference that does not start with…
- shutdown_complete_tripwire for source
- Could not build Datadog domain regex
- `duration` should be an i64
- `error` should be an i64
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/4b6c14f3d8a06a21.
Report an issue: GitHub.
Appendix: source
Thrown at lib/vector-config-macros/src/configurable.rs:220
variants
.iter()
.filter(|variant| variant.tagging() == &Tagging::None)
.count()
>= 2
}
}
}
fn build_struct_generate_schema_fn(
container: &Container<'_>,
style: &Style,
fields: &[Field<'_>],
) -> proc_macro2::TokenStream {
match style {
Style::Struct => build_named_struct_generate_schema_fn(container, fields),
Style::Tuple => build_tuple_struct_generate_schema_fn(fields),
Style::Newtype => build_newtype_struct_generate_schema_fn(fields),
Style::Unit => panic!("unit structs should be rejected during AST parsing"),
}
}
fn generate_struct_field(field: &Field<'_>, sibling_keys: &[String]) -> proc_macro2::TokenStream {
let field_metadata_ref = Ident::new("field_metadata", Span::call_site());
let field_metadata = generate_field_metadata(&field_metadata_ref, field);
let field_schema_ty = get_field_schema_ty(field);
// Flattened `Option<T>` cannot use `Option`'s nullable-property schema: `allOf` merge
// validates the parent object, which is never JSON `null`. Tagged enums get an absence
// encoding instead; other `Option<T>` flatten fields fall back to the property schema.
let spanned_generate_schema = match (field.flatten(), option_inner_type(field_schema_ty)) {
(true, Some(inner_ty)) => {
let sibling_key_lits = sibling_keys.iter().map(|key| quote! { #key });
quote_spanned! {field.span()=>
::vector_config::schema::generate_flattened_optional_schema(
&<#inner_ty as ::vector_config::Configurable>::as_configurable_ref(),
&<#field_schema_ty as ::vector_config::Configurable>::as_configurable_ref(),View on GitHub (pinned to bdb87aeaa4)