pydantic/monty · error · syn::Error

no fields may appear after a `#[from_args(varkwargs)]` field

Error message

no fields may appear after a `#[from_args(varkwargs)]` field

What it means

A compile-time error from the `FromArgs` derive's field-role resolution: `**kwargs` must be the last parameter, mirroring Python, so no struct field may be declared after a `#[from_args(varkwargs)]` field. Any trailing field is rejected at the field's span.

Source

Thrown at crates/monty-macros/src/from_args.rs:555

            .filter(|f| !matches!(f.kind, FieldKind::Varargs | FieldKind::Varkwargs))
    }
}

/// Second parse pass over the fields: resolve implicit kw_only-after-varargs,
/// enforce declaration ordering, assign positional and slot indices, and
/// locate the `*args` / `**kwargs` fields.
fn resolve_field_roles(fields: &mut [Field]) -> syn::Result<(Option<usize>, Option<usize>)> {
    let mut varargs_idx = None;
    let mut varkwargs_idx = None;
    let mut seen_varargs = false;
    let mut seen_varkwargs = false;
    let mut seen_pos_or_kw = false;
    let mut seen_kw_only = false;
    let mut pos_counter: usize = 0;
    let mut slot_counter: usize = 0;
    for (idx, field) in fields.iter_mut().enumerate() {
        if seen_varkwargs {
            return Err(syn::Error::new(
                field.ident.span(),
                "no fields may appear after a `#[from_args(varkwargs)]` field",
            ));
        }

        match field.kind {
            FieldKind::PosOnly => {
                if seen_pos_or_kw || seen_kw_only || seen_varargs {
                    return Err(syn::Error::new(
                        field.ident.span(),
                        "positional-only fields must come before positional-or-keyword, varargs, and keyword-only fields",
                    ));
                }
            }
            FieldKind::PosOrKeyword => {
                if seen_varargs {
                    // Implicit kw_only after varargs.
                    field.kind = FieldKind::KwOnly;

View on GitHub (pinned to adc986b362)

Solutions

  1. Move the `#[from_args(varkwargs)]` field to be the last field of the struct
  2. Move the trailing field before the varkwargs field (respecting required-before-default ordering for positionals)
  3. If the trailing field is really positional, restructure the signature so kwargs remains last

Example fix

// before
#[derive(FromArgs)]
struct Args {
    #[from_args(varkwargs)]
    options: Vec<(String, Value)>,
    name: String, // after varkwargs — rejected
}
// after
#[derive(FromArgs)]
struct Args {
    name: String,
    #[from_args(varkwargs)]
    options: Vec<(String, Value)>,
}
Defensive patterns

Strategy: validation

Validate before calling

// varkwargs must be the final field of the struct
struct Args { name: String, #[from_args(varkwargs)] opts: Vec<(String, Value)> }

Prevention

When it happens

Trigger: Declaring any field after the one marked `#[from_args(varkwargs)]`, e.g. `#[from_args(varkwargs)] options: ...` followed by `name: String`.

Common situations: Reordering struct fields alphabetically or via an IDE sort; appending a new parameter to the end of the struct without noticing the collector field; porting a Python signature where `**kwargs` is last but the struct drifted.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/647992fd017e2317. Report an issue: GitHub.