vectordotdev/vector · error
inner value must be a map
Error message
inner value must be a map
What it means
TraceEvent is a newtype over LogEvent, and into_parts() assumes the inner event value is a Value::Map, calling value.into_object().expect("inner value must be a map") (lib/vector-core/src/event/trace.rs). LogEvent is normally always constructed with a map (from_map/from_parts), so this panic fires only when a TraceEvent was materialized through a path that can hold a non-map value — e.g. deserializing a scalar into the inner LogEvent — violating the newtype's invariant.
Source
Thrown at lib/vector-core/src/event/trace.rs:28
use vrl::path::PathParseError;
use super::{
BatchNotifier, EstimatedJsonEncodedSizeOf, EventFinalizer, EventFinalizers, EventMetadata,
Finalizable, LogEvent, MergeFinalizable, ObjectMap, Value,
};
/// Traces are a newtype of `LogEvent`
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct TraceEvent(LogEvent);
impl TraceEvent {
/// Convert a `TraceEvent` into a tuple of its components
/// # Panics
///
/// Panics if the fields of the `TraceEvent` are not a `Value::Map`.
pub fn into_parts(self) -> (ObjectMap, EventMetadata) {
let (value, metadata) = self.0.into_parts();
let map = value.into_object().expect("inner value must be a map");
(map, metadata)
}
pub fn from_parts(fields: ObjectMap, metadata: EventMetadata) -> Self {
Self(LogEvent::from_map(fields, metadata))
}
pub fn value(&self) -> &Value {
self.0.value()
}
pub fn value_mut(&mut self) -> &mut Value {
self.0.value_mut()
}
pub fn metadata(&self) -> &EventMetadata {
self.0.metadata()
}View on GitHub (pinned to 3708c39b12)
Solutions
- Validate the payload shape before deserializing: require the top-level JSON value to be an object before constructing a TraceEvent
- Build TraceEvents only via TraceEvent::from_parts(ObjectMap, metadata) or insertion APIs that keep the map invariant
- If you deserialize untrusted data, match on the Value first and reject non-map values with an error instead of proceeding
Example fix
// before
let (map, metadata) = trace.into_parts();
// after
let (value, metadata) = trace.into_parts_value();
let Some(map) = value.into_object() else {
return Err("trace event fields must be an object");
}; Defensive patterns
Strategy: type-guard
Validate before calling
// Before calling into_parts, confirm the payload shape if it came from external data
if !raw_json.is_object() {
return Err(DecodeError::new("trace payload must be a JSON object"));
} Type guard
fn trace_is_map(trace: &TraceEvent) -> bool {
matches!(trace.value(), Value::Map(_))
} Prevention
- Only construct TraceEvent via from_parts(ObjectMap, metadata)
- Validate external payloads are objects at the trust boundary before deserializing into typed events
- In tests, use TraceEvent::from_parts fixtures instead of serde-deserialized scalars
When it happens
Trigger: Calling TraceEvent::into_parts() on a TraceEvent whose inner LogEvent value is not Value::Map — typically a TraceEvent built via serde Deserialize from JSON that is a string/number/array rather than an object, or via LogEvent::new with a manually replaced scalar value. from_parts()-built events can never trigger it.
Common situations: Test code or embedding code that deserializes TraceEvents from payloads where the trace body is not a JSON object; interop code that converts arbitrary Values into TraceEvent without a map check. End-user Vector configs do not hit this in normal operation — it is an API-misuse/serialization-shape panic.
Related errors
- argument must be a string
- argument must be a string
- key must be a string
- secret must be a string
- argument must be a string
AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20).
Data as JSON: /api/errors/4fbf3dcdf3eb6003.
Report an issue: GitHub.