vectordotdev/vector · error

schema metadata must always be an object

Error message

schema metadata must always be an object

What it means

`set_schema_metadata_attr_str` inserts a metadata entry into a schema object's extensions and then asserts it is a JSON object before inserting the key. The panic fires when the existing metadata extension value is not a JSON object (e.g. it is a string, number, or array), so `as_object_mut()` returns `None`.

Solutions

  1. Check any custom `Configurable` implementations for code writing the `metadata` extension key directly and make them write a JSON object
  2. Ensure all crates in the workspace use the same vector-config version so the metadata convention matches
  3. Reproduce with a minimal config schema and inspect the generated extensions map before the visitor runs
  4. Reset to stock vector-config types to confirm the bug is in custom code

Example fix

// before
metadata.extensions.insert(constants::METADATA.to_string(), Value::String("my field".into()));
// after
metadata.extensions.insert(constants::METADATA.to_string(), json!({}));
Defensive patterns

Strategy: type-guard

Validate before calling

fn ensure_metadata_object(extensions: &Map<String, Value>) -> Result<(), String> {
    match extensions.get(constants::METADATA) {
        Some(v) if !v.is_object() => Err(format!("metadata extension must be an object, got {v}")),
        _ => Ok(()),
    }
}

Type guard

fn as_metadata_object(v: &Value) -> Option<&serde_json::Map<String, Value>> { v.as_object() }

Try / catch

let metadata_map = metadata.as_object_mut().ok_or_else(||
    anyhow::anyhow!("schema metadata extension is not a JSON object"))?;

Prevention

When it happens

Trigger: A schema's extensions map already contains a `metadata` key whose value is not a JSON object, then a visitor such as `visit_schema_object` calls `set_schema_metadata_attr_str` to add a human-readable name; or a custom `Configurable` type writes `constants::METADATA` as a non-object value.

Common situations: Hand-rolled `Configurable` implementations writing malformed custom attributes into schema metadata; merging schemas generated by different schemars/vector-config versions where the metadata convention differs.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16). Data as JSON: /api/errors/8ecfd4fc1fc8e024. Report an issue: GitHub.

Appendix: source

Thrown at lib/vector-config/src/schema/visitors/human_name.rs:100

}

fn get_schema_metadata_attr_str<'a>(schema: &'a SchemaObject, key: &str) -> Option<&'a str> {
    schema
        .extensions
        .get(constants::METADATA)
        .and_then(|metadata| metadata.get(key))
        .and_then(|value| value.as_str())
}

fn set_schema_metadata_attr_str(schema: &mut SchemaObject, key: &str, value: String) {
    let metadata = schema
        .extensions
        .entry(constants::METADATA.to_string())
        .or_insert_with(|| Value::Object(serde_json::Map::new()));

    let metadata_map = metadata
        .as_object_mut()
        .expect("schema metadata must always be an object");
    metadata_map.insert(key.to_string(), Value::String(value));
}

#[cfg(test)]
mod tests {
    use serde_json::json;
    use vector_config_common::schema::visit::Visitor;

    use super::GenerateHumanFriendlyNameVisitor;
    use crate::schema::visitors::test::{as_schema, assert_schemas_eq};

    #[test]
    fn logical_name() {
        let mut actual_schema = as_schema(json!({
            "type": "string",
            "_metadata": {
                "logical_name": "LogToMetric"
            }

View on GitHub (pinned to bdb87aeaa4)