pydantic/monty · error · syn::Error

required positional fields must come before positional…

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.

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.

Appendix: 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)