pydantic/monty · error · syn::Error

required positional fields must come before positional field

Error message

required positional fields must come before positional fields with defaults — matching Python signatures, and relied on by the runtime binder's fast path

What it means

A compile-time validation error from the `FromArgs` derive: within positional parameters (pos-only or pos-or-keyword), a required field may not follow a field with a `default`. Python signatures impose the same ordering, and the runtime binder's fast path relies on it to locate required positionals.

Source

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

            if self.kwarg_error_name.is_some() {
                return err("`kwargs_not_supported_yet` cannot be combined with `kwarg_error_name` \
                     — the override only applies to the unknown-kwarg dispatch path, which is skipped");
            }
        }

        // The runtime binder's fast path fills the first `n` positional slots
        // and assumes that satisfies every required positional param — sound
        // only if required positional fields precede defaulted ones (the same
        // ordering Python enforces for `def` signatures).
        let mut seen_positional_default = false;
        for field in &self.fields {
            if !matches!(field.kind, FieldKind::PosOnly | FieldKind::PosOrKeyword) {
                continue;
            }
            if field.default.is_some() {
                seen_positional_default = true;
            } else if seen_positional_default {
                return Err(syn::Error::new(
                    field.ident.span(),
                    "required positional fields must come before positional fields with \
                     defaults — matching Python signatures, and relied on by the runtime \
                     binder's fast path",
                ));
            }
        }

        // Raw binding is deliberately separate from conversion (that split is
        // what reproduces CPython's error orderings), so `*args` elements are
        // handed over unconverted.
        if let Some(idx) = self.varargs_idx
            && !is_vec_of_value(&self.fields[idx].ty)
        {
            return Err(syn::Error::new(
                self.fields[idx].ident.span(),
                "`varargs` fields must be `Vec<Value>` — coerce elements in the function body",
            ));

View on GitHub (pinned to adc986b362)

Solutions

  1. Reorder the struct fields so all required positional fields come first
  2. Give the later field a `default` if it can be optional
  3. Mark the defaulted field `kw_only` (with a default, per the binder's constraints) instead of positional

Example fix

// before
#[derive(FromArgs)]
struct Args {
    #[from_args(default)]
    timeout: u64,
    path: String, // required after defaulted — rejected
}
// after
#[derive(FromArgs)]
struct Args {
    path: String,
    #[from_args(default)]
    timeout: u64,
}
Defensive patterns

Strategy: validation

Validate before calling

// compile-time convention: keep required positional fields above defaulted ones
struct Args { required1: String, required2: u32, #[from_args(default)] opt: u64 }

Prevention

When it happens

Trigger: Declaring `#[derive(FromArgs)]` with fields ordered like `a: u32` (default), `b: u32` (required), where both are positional.

Common situations: Alphabetically or accidentally reordering struct fields; inserting a new required parameter after existing defaulted ones; porting a C signature whose ordering the derive cannot mirror.

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/67f1b7d0284644a5. Report an issue: GitHub.