{"record":{"id":"a3fc0914054449f9","repo":"BoundaryML/baml","slug":"unions-must-be-flattened","errorCode":null,"errorMessage":"Unions must be flattened","messagePattern":"Unions must be flattened","errorType":"error_code","errorClass":"ConvertError","httpStatus":null,"severity":"error","filePath":"baml_language/crates/bex_sap/src/sap_model/convert.rs","lineNumber":44,"sourceCode":"    #[error(\"Unknown media kind\")]\n    UnknownMediaKind,\n    #[error(\"Float literals cannot be parsed\")]\n    FloatLiteral,\n    #[error(\"Non-parsable type: {0:?}\")]\n    NonParsableType(Box<SapTy>),\n    #[error(\"Unknown class: {0}\")]\n    UnknownClass(DefKey),\n    #[error(\"Unknown enum: {0}\")]\n    UnknownEnum(DefKey),\n    #[error(\"Unknown type alias: {0}\")]\n    UnknownTypeAlias(DefKey),\n    #[error(\"Unknown name (could not determine if it was a class, enum, or type alias): {0}\")]\n    UnknownName(DefKey),\n    #[error(\"Could not add a type to the database as the name `{0}` is already present\")]\n    AlreadyPresent(DefKey),\n    #[error(\"Recursion depth exceeded for {0}\")]\n    RecursionDepthExceeded(&'static str),\n    #[error(\"Unions must be flattened\")]\n    UnflattenedUnion,\n    /// Something like `type A = B; type B = A;` is invalid.\n    #[error(\"Recursive type alias without indirection: {0}\")]\n    DirectRecursiveTypeAlias(DefKey),\n    #[error(\"Internal error (please report): {0}\")]\n    InternalError(&'static str),\n}\n\nconst MAX_RECURSION_DEPTH: usize = 16;\n\n/// Contains stuff from [`sys_types::SysOpContext`] that we need for converting to the sap model.\n///\n/// ## Representation\n/// - Unions should be flattened:\n///   - Union members cannot be unions\n///   - Union members cannot be optional\n///   - Union members cannot be type aliases which themselves resolve to unions (or optionals)\n///   - Same rules for the inner type of an optional type","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bex_sap/src/sap_model/convert.rs#L26-L62","documentation":"ConvertError::UnflattenedUnion is raised in convert_ty when a SapTy::Union contains a member that is itself union-like — a nested union or a type alias that resolves to a union. The SAP model representation requires unions to be fully flattened: members must not be unions or optional, and aliases resolving to unions are not permitted.","triggerScenarios":"TypeCtx::build_db converting a union whose items include a nested SapTy::Union or an alias whose definition is union-like; notably normalize_parse_target can create fresh nested unions like (string | int) | ToolCalls at runtime after generic substitution.","commonSituations":"Runtime-materialized parse targets after generic substitution bypassing TypeCtx::new's simplification; hand-constructed types that skip the simplify step; aliases aliasing other union aliases.","solutions":["Ensure types pass through baml_type::simplify_sap::simplify / normalize_parse_target before conversion so nested unions are flattened.","Rewrite the type to a single flat union, e.g. (string | int) | Foo becomes string | int | Foo.","Avoid aliases that resolve to unions inside other unions; inline the union members directly."],"exampleFix":"// before\ntype StringsOrInts = string | int\nfunction F() -> StringsOrInts | bool // nested union\n// after\nfunction F() -> string | int | bool // flattened","handlingStrategy":"validation","validationCode":"// A union member must not be a union or an alias resolving to one\nfn union_members_flat(ty: &SapTy, defs: &HashMap<DefKey, SapTy>) -> bool {\n    match ty {\n        SapTy::Union(items, _) => items.iter().all(|i| !matches!(i, SapTy::Union(..))),\n        SapTy::TypeAlias(name, _) => !matches!(defs.get(name), Some(SapTy::Union(..))),\n        _ => true,\n    }\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(ConvertError::UnflattenedUnion) => eprintln!(\"flatten nested unions before conversion\"),\n    other => other,\n}","preventionTips":["Always run types through simplify_sap::simplify before conversion.","Write unions flat: string | int | Foo, never (string | int) | Foo.","Avoid aliases that resolve to unions nested inside other unions."],"tags":["sap","baml","union","type-conversion","normalization"],"backgroundTag":"incompatible-source-type","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}