BoundaryML/baml · error · syn::Error

variant `{}` requires an explicit discriminant to stabilize

Error message

variant `{}` requires an explicit discriminant to stabilize its Borsh tag

What it means

Type-family variants must carry an explicit `u8` integer discriminant so the Borsh serialization tag is stable. `resolve_variant` raises this error when the discriminant expression is absent (the `None` arm). A present but non-u8-literal discriminant gets the separate 'type-family variants require an explicit u8 integer discriminant' error.

Source

Thrown at baml_language/crates/baml_type_macros/src/parse.rs:433

                "variant `{}` must declare exactly one `#[axis(..)]`",
                variant.ident
            ),
        )
    })?;
    let axis = axis_index(&axis_ident)?;
    let discriminant = match variant.discriminant.as_ref().map(|(_, expr)| expr) {
        Some(syn::Expr::Lit(syn::ExprLit {
            lit: syn::Lit::Int(value),
            ..
        })) => value.base10_parse::<u8>()?,
        Some(expr) => {
            return Err(syn::Error::new_spanned(
                expr,
                "type-family variants require an explicit u8 integer discriminant",
            ));
        }
        None => {
            return Err(syn::Error::new(
                span,
                format!(
                    "variant `{}` requires an explicit discriminant to stabilize its Borsh tag",
                    variant.ident
                ),
            ));
        }
    };
    // A variant need not carry a `TyAttr`: a template-only leaf (`TypeArgRef`)
    // is pure structure with no streaming metadata. The generated
    // `attr()`/`with_attr()` accessors fall back to `TyAttr::EMPTY` / identity
    // for them (see `emit::attr_arm`). `has_attr` records which case applies so
    // the accessor arms don't need to re-derive it.
    let has_attr = carries_ty_attr(&variant.fields);
    Ok(MVariant {
        attrs,
        ident: variant.ident,
        fields: variant.fields,

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Add an explicit integer discriminant to the variant, e.g. `Variant = 4`.
  2. Pick an unused u8 value consistent with the other variants' numbering.
  3. Never reuse a discriminant already assigned to another variant.

Example fix

// before
#[axis(Shape)]
Variant { x: u32 },
// after
#[axis(Shape)]
Variant = 5 { x: u32 },
Defensive patterns

Strategy: validation

Validate before calling

// every variant needs an explicit u8 discriminant
// enum TypeFamily { A = 0, B = 1, /* ... */ }

Prevention

When it happens

Trigger: Declaring a variant with `#[axis(..)]` but no explicit discriminant like `= 3`; the discriminant match on the variant's expr returns `None`.

Common situations: Adding a variant and omitting the discriminant because plain Rust enums allow implicit numbering; implicit tags would break wire compatibility if variants are reordered.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/3e495a76d6563470. Report an issue: GitHub.