BoundaryML/baml · error · ConvertError

Recursion depth exceeded for {0}

Error message

Recursion depth exceeded for {0}

What it means

ConvertError::RecursionDepthExceeded is raised when type-alias resolution or field-attribute derivation recurses past MAX_RECURSION_DEPTH (16). This guards against runaway recursion through chains of type aliases or nested unions during SAP model conversion. The payload string describes which derivation exceeded the limit (e.g. "type alias", "class field nullability derivation").

Source

Thrown at baml_language/crates/bex_sap/src/sap_model/convert.rs:42

    #[error("Failed to parse float: {0}")]
    ParseFloat(#[from] std::num::ParseFloatError),
    #[error("Unknown media kind")]
    UnknownMediaKind,
    #[error("Float literals cannot be parsed")]
    FloatLiteral,
    #[error("Non-parsable type: {0:?}")]
    NonParsableType(Box<SapTy>),
    #[error("Unknown class: {0}")]
    UnknownClass(DefKey),
    #[error("Unknown enum: {0}")]
    UnknownEnum(DefKey),
    #[error("Unknown type alias: {0}")]
    UnknownTypeAlias(DefKey),
    #[error("Unknown name (could not determine if it was a class, enum, or type alias): {0}")]
    UnknownName(DefKey),
    #[error("Could not add a type to the database as the name `{0}` is already present")]
    AlreadyPresent(DefKey),
    #[error("Recursion depth exceeded for {0}")]
    RecursionDepthExceeded(&'static str),
    #[error("Unions must be flattened")]
    UnflattenedUnion,
    /// Something like `type A = B; type B = A;` is invalid.
    #[error("Recursive type alias without indirection: {0}")]
    DirectRecursiveTypeAlias(DefKey),
    #[error("Internal error (please report): {0}")]
    InternalError(&'static str),
}

const MAX_RECURSION_DEPTH: usize = 16;

/// Contains stuff from [`sys_types::SysOpContext`] that we need for converting to the sap model.
///
/// ## Representation
/// - Unions should be flattened:
///   - Union members cannot be unions
///   - Union members cannot be optional

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Flatten or shorten the alias chain so references resolve in fewer than 16 hops.
  2. Replace deep alias chaining with a direct reference to the underlying type.
  3. If legitimately deep nesting is required, restructure it into intermediate classes instead of alias chains.

Example fix

// before
type A = B
type B = C
// ... 20 levels ...
type T = string
// after
type A = string // collapse the chain
Defensive patterns

Strategy: validation

Validate before calling

// Count alias-chain depth before conversion; bail early if it approaches 16
fn alias_chain_depth(defs: &HashMap<DefKey, SapTy>, mut cur: DefKey) -> usize {
    let mut depth = 0;
    while let Some(SapTy::TypeAlias(next, _)) = defs.get(&cur) {
        depth += 1;
        cur = next.clone();
    }
    depth
}

Try / catch

match result {
    Err(ConvertError::RecursionDepthExceeded(what)) => eprintln!("shorten the {what} chain to <= 16 levels"),
    other => other,
}

Prevention

When it happens

Trigger: convert_type_alias recursing beyond depth 16 through chained aliases; get_field_attrs or field_type_is_nullable_inner walking an alias/union chain deeper than 16 levels.

Common situations: Very long alias chains (type A1 = A2; type A2 = A3; ...); deeply nested union types produced by runtime generic substitution; generated code that programmatically nests types.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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