vectordotdev/vector · error · syn::Error

`required_one_of` cannot be applied to a `#[serde(skip_deser

Error message

`required_one_of` cannot be applied to a `#[serde(skip_deserializing)]` field; users cannot set its value

What it means

Compile-time error from `build_named_struct_generate_schema_fn`. `#[serde(skip_deserializing)]` means the field's value is never taken from user config (it is populated by code after deserialization), so users cannot satisfy a `required_one_of` group through it. The macro rejects this combination explicitly.

Source

Thrown at lib/vector-config-macros/src/configurable.rs:394

            if field.flatten() {
                return syn::Error::new(
                    field.span(),
                    "`required_one_of` cannot be applied to a `#[serde(flatten)]` field",
                )
                .to_compile_error();
            }
            if !is_option_type(field.ty())
                && field.default_value().is_none()
                && container.default_value().is_none()
            {
                return syn::Error::new(
                    field.span(),
                    "`required_one_of` requires the field to be optional; use `Option<T>` or add `#[serde(default)]`",
                )
                .to_compile_error();
            }
            if field.skip_deserializing() {
                return syn::Error::new(
                    field.span(),
                    "`required_one_of` cannot be applied to a `#[serde(skip_deserializing)]` field; users cannot set its value",
                )
                .to_compile_error();
            }
        }
    }

    // Collect required_one_of groups at macro-expansion time: group_name -> [serde field names].
    let mut groups: std::collections::BTreeMap<String, Vec<String>> =
        std::collections::BTreeMap::new();
    for field in fields.iter().filter(|f| f.visible()) {
        if let Some(group) = field.required_one_of() {
            groups
                .entry(group.to_string())
                .or_default()
                .push(field.name().to_string());
        }

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Remove `required_one_of` from the skip_deserializing field
  2. Redesign the group so it only contains fields users can actually set in config
  3. If the field is user-settable after all, remove `#[serde(skip_deserializing)]` and make it optional

Example fix

// before
#[configurable(required_one_of = ["auth"])]
#[serde(skip_deserializing)]
pub auth: AuthState,

// after
#[serde(skip_deserializing)]
pub auth: AuthState, // excluded from the group; group uses user-set fields
Defensive patterns

Strategy: validation

Validate before calling

# CI gate: skip_deserializing conflicts fail at compile time
cargo check --workspace --all-targets

Prevention

When it happens

Trigger: A field annotated with both `#[serde(skip_deserializing)]` (e.g. computed/derived fields like a client handle or resolved endpoint) and `#[configurable(required_one_of(...))]`.

Common situations: Config structs that carry runtime-resolved state alongside user options; during a schema-attributes refactor the group attribute is left on the now-internal field.

Related errors


AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20). Data as JSON: /api/errors/688a4975bc2e465a. Report an issue: GitHub.