vectordotdev/vector · error
UNSUPPORTED_YAML_MERGE_KEY
Error message
UNSUPPORTED_YAML_MERGE_KEY
What it means
When parsing a YAML feature config, load_and_extract scans the deserialized serde_yaml::Value for merge keys (`<<:` or `<<-`). serde_yaml does not resolve YAML merge keys into the target struct, so configs relying on them would silently lose fields; the tool proactively bails with the UNSUPPORTED_YAML_MERGE_KEY message rather than produce wrong feature lists.
Solutions
- Remove the merge key and inline the merged fields explicitly
- Resolve the anchors manually: replace `<<: *base` with the full copied key/value pairs
- Rewrite the file in TOML or JSON, which have no merge-key semantics
Example fix
// before
sinks:
my_sink:
<<: *common
type: console
// after
sinks:
my_sink:
type: console
inputs: ["*"]
encoding: json Defensive patterns
Strategy: validation
Validate before calling
fn uses_merge_key(yaml_text: &str) -> bool {
yaml_text.lines().any(|l| l.trim_start().starts_with("<<:"))
}
if uses_merge_key(&text) { panic!("resolve YAML merge keys before processing"); } Prevention
- Avoid `<<:` merge keys in Vector feature configs; inline shared fields
- Prefer TOML or JSON for configs generated by scripts
- When copying YAML from Kubernetes/Helm, expand anchors first
When it happens
Trigger: Calling load_and_extract on a .yaml/.yml file whose mapping (at any nesting depth) contains a `<<:` merge key, typically used with YAML anchors to share component options.
Common situations: Copying a Kubernetes-style YAML config that uses anchors-and-merge-keys DRY patterns into a Vector feature config; refactoring shared sink settings with `<<: *defaults`.
Related errors
- Deprecation fragment
- UNSUPPORTED_PROVIDER
- component name must be non-empty
- Deprecation fragment
- Deprecation fragment
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/3d48e36ecea4c678.
Report an issue: GitHub.
Appendix: source
Thrown at vdev/src/utils/features.rs:127
}
pub fn load_and_extract(filename: &Path) -> Result<Vec<String>> {
let config = fs::read_to_string(filename)
.with_context(|| format!("failed to read {}", filename.display()))?;
let config: FeatureConfig = match filename
.extension()
.and_then(OsStr::to_str)
.map(str::to_lowercase)
.as_deref()
{
None => bail!("Invalid filename {}, no extension", filename.display()),
Some("json") => serde_json::from_str(&config)?,
Some("toml") => toml::from_str(&config)?,
Some("yaml" | "yml") => {
let value: serde_yaml::Value = serde_yaml::from_str(&config)?;
if contains_yaml_merge_key(&value) {
bail!(UNSUPPORTED_YAML_MERGE_KEY);
}
serde_yaml::from_value(value)?
}
Some(_) => bail!("Invalid filename {}, unknown extension", filename.display()),
};
let declared_features = CargoToml::load_from(&find_repo_root()?.join("Cargo.toml"))?
.features
.into_keys()
.collect();
from_config(&config, &declared_features)
}
fn contains_yaml_merge_key(value: &serde_yaml::Value) -> bool {
match value {
serde_yaml::Value::Sequence(values) => values.iter().any(contains_yaml_merge_key),
serde_yaml::Value::Mapping(mapping) => mappingView on GitHub (pinned to bdb87aeaa4)