vectordotdev/vector · error
Could not convert number to JSON
Error message
Could not convert number to JSON
What it means
The `ToValue` implementation for `f32`/`f64` converts a float to a `serde_json::Number` via `Number::from_f64`, which returns `None` for non-finite values (NaN, +inf, -inf) since JSON cannot represent them, and then panics. It signals that a floating-point config value being serialized to the event/value model is not a valid JSON number.
Solutions
- Validate the float is finite before converting: `assert!(v.is_finite())` or return an error
- Clamp or replace non-finite values with a sentinel (e.g. 0.0 or a configured default) at config-parse time
- Fix the source computation producing NaN/inf (division by zero, overflow)
- If parsing user/env input, reject non-finite strings during config validation
Example fix
// before
let ratio = numerator / denominator;
config.threshold = ratio;
// after
let ratio = if denominator == 0.0 { 0.0 } else { numerator / denominator };
assert!(ratio.is_finite(), "threshold must be a finite number");
config.threshold = ratio; Defensive patterns
Strategy: validation
Validate before calling
fn ensure_finite_json_number(v: f64) -> Result<f64, String> {
if v.is_finite() { Ok(v) } else { Err(format!("value {v} is not a valid JSON number")) }
} Type guard
fn is_json_safe_f64(v: &f64) -> bool { v.is_finite() } Try / catch
match Number::from_f64(v) {
Some(n) => Value::Number(n),
None => return Err(anyhow::anyhow!("cannot serialize non-finite float {v}")),
} Prevention
- Check `is_finite()` on any computed float before storing it in config or events
- Reject "inf"/"nan" strings when parsing numeric config from user input
- Guard divisions by zero and multiplication overflow in default-value computations
When it happens
Trigger: Calling `to_value()` on an `f32`/`f64` that is NaN or infinite — e.g. a config value computed at runtime, a default derived from a division by zero, or an instrument/metric constant built from non-finite math.
Common situations: Config values computed as `0.0/0.0` (NaN) or overflow to infinity (e.g. `f64::MAX * 2.0`); parsing user input like "inf"/"nan" into f64 config fields; interpolating environment-driven defaults that produce non-finite results.
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
- Could not convert concurrency to JSON
- Could not convert HTTP status code to JSON
- Could not convert time zone to JSON
- Could not convert TOML value to JSON
- FunctionDoc serialization should not fail
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/6f42d9fa5a006359.
Report an issue: GitHub.
Appendix: source
Thrown at lib/vector-config/src/stdlib.rs:201
fn to_value(&self) -> Value {
let into = $into;
Value::Number(into(*self))
}
}
};
}
impl_configurable_numeric!(u8 => Into::into);
impl_configurable_numeric!(u16 => Into::into);
impl_configurable_numeric!(u32 => Into::into);
impl_configurable_numeric!(u64 => Into::into);
impl_configurable_numeric!(usize => Into::into);
impl_configurable_numeric!(i8 => Into::into);
impl_configurable_numeric!(i16 => Into::into);
impl_configurable_numeric!(i32 => Into::into);
impl_configurable_numeric!(i64 => Into::into);
impl_configurable_numeric!(isize => Into::into);
impl_configurable_numeric!(f32 => |v| Number::from_f64(v as f64).expect("Could not convert number to JSON"));
impl_configurable_numeric!(f64 => |v| Number::from_f64(v).expect("Could not convert number to JSON"));
impl_configurable_numeric!(NonZeroU8 => |v: NonZeroU8| v.get().into());
impl_configurable_numeric!(NonZeroU16 => |v: NonZeroU16| v.get().into());
impl_configurable_numeric!(NonZeroU32 => |v: NonZeroU32| v.get().into());
impl_configurable_numeric!(NonZeroU64 => |v: NonZeroU64| v.get().into());
impl_configurable_numeric!(NonZeroI8 => |v: NonZeroI8| v.get().into());
impl_configurable_numeric!(NonZeroI16 => |v: NonZeroI16| v.get().into());
impl_configurable_numeric!(NonZeroI32 => |v: NonZeroI32| v.get().into());
impl_configurable_numeric!(NonZeroI64 => |v: NonZeroI64| v.get().into());
impl_configurable_numeric!(NonZeroUsize => |v: NonZeroUsize| v.get().into());
// Arrays and maps.
impl<T> Configurable for Vec<T>
where
T: Configurable + ToValue + 'static,
{
fn metadata() -> Metadata {
T::metadata().convert()View on GitHub (pinned to bdb87aeaa4)