pydantic/monty · error · syn::Error
`bad_arg`/`bad_arg_named` cannot be combined with `style = d
Error message
`bad_arg`/`bad_arg_named` cannot be combined with `style = def` — CPython `def` binding never type-checks while binding; declare fields as raw `Value` and coerce in the function body
What it means
A `#[derive(FromArgs)]` struct declared `style = def` together with a `bad_arg` or `bad_arg_named` attribute. This compile-time check enforces CPython semantics: `def`-style binding never type-checks or raises bad-argument errors while binding (too-few/invalid args are caught later at coercion), so a bad-argument override has nothing to attach to. The fix is to declare the affected fields as raw `Value` and do the type check in the function body.
Source
Thrown at crates/monty-macros/src/from_args.rs:217
vectorcall,
kwarg_error_name,
bad_arg,
kwargs_not_supported_yet,
};
signature.validate()?;
Ok(signature)
}
/// Style/modifier/field compatibility checks — the single place invalid
/// combinations are rejected. Grouped per style, then the orthogonal
/// modifiers, so each rule reads as one line of the compatibility table.
fn validate(&self) -> syn::Result<()> {
let err = |msg: &str| Err(syn::Error::new(self.struct_ident.span(), msg));
match self.style {
Style::Def => {
if self.bad_arg.is_some() {
return err("`bad_arg`/`bad_arg_named` cannot be combined with `style = def` \
— CPython `def` binding never type-checks while binding; declare \
fields as raw `Value` and coerce in the function body");
}
if self.varargs_idx.is_some() {
return err("`style = def` cannot be combined with `varargs` — a `*args` \
signature can never raise too-many-positional, so the style has no effect");
}
}
Style::Unpack => {
if self.fields.iter().any(|f| matches!(f.kind, FieldKind::PosOrKeyword)) {
return err("`style = unpack` models a positional-only `PyArg_UnpackTuple` \
signature — every positional field must be `pos_only`");
}
if self.varargs_idx.is_some() || self.varkwargs_idx.is_some() {
return err("`style = unpack` cannot be combined with `varargs` or `varkwargs` \
— it models a fixed positional min..max range");
}
}View on GitHub (pinned to adc986b362)
Solutions
- Remove the `bad_arg`/`bad_arg_named` attribute from the struct when using `style = def`.
- If you need a custom bad-argument message, switch the style back to the default `clinic` (or a C family) where `bad_arg` is allowed.
- Keep `style = def`, declare the field as `Value` instead of the concrete type, and perform the type check/coercion manually in the function body, raising the desired error there.
Example fix
// before
#[derive(FromArgs)]
#[from_args(style = "def", bad_arg = "my_func")]
struct Args { count: i64 }
// after
#[derive(FromArgs)]
#[from_args(style = "def")]
struct Args { count: Value }
// in the body: coerce and raise your own error
let count = args.count.get_int()?; Defensive patterns
Strategy: validation
Validate before calling
// Struct-level attribute check before compiling:
// ensure any struct with style = "def" has no bad_arg / bad_arg_named in its #[from_args(...)] list.
fn uses_def_style_and_bad_arg(attrs: &[&str]) -> bool {
let def = attrs.contains(&"style = \"def\"");
let bad = attrs.iter().any(|a| a.starts_with("bad_arg"));
def && bad
} Prevention
- Remember the rule: def-style binding defers type errors to coercion — any custom bad-argument handling lives in the function body, not the macro attributes.
- Read crates/monty-macros/README.md's style table before mixing attributes.
- When porting a struct between styles, audit its full #[from_args(...)] attribute list.
When it happens
Trigger: Compiling a struct annotated `#[derive(FromArgs)] #[from_args(style = "def", bad_arg = "...")]` (or `bad_arg_named = "..."`). Exactly this combination, checked in `Signature::validate` at crates/monty-macros/src/from_args.rs:216-220.
Common situations: Porting a handler from the default `clinic` style to `style = def` to get CPython def-binding error behavior while leaving the old `bad_arg` override in place; copy-pasting a struct that used `bad_arg` under a C-family style; misunderstanding that `def` styles defer errors to coercion rather than binding.
Related errors
- `style = def` cannot be combined with `varargs` — a `*args`
- `style = unpack` models a positional-only `PyArg_UnpackTuple
- `style = unpack` cannot be combined with `varargs` or `varkw
- `at_most_total` cannot be combined with `style = def` or `st
- `at_most_total` cannot be combined with `varargs` or `varkwa
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/04d5a9ea847df49f.
Report an issue: GitHub.