BoundaryML/baml · error · CtypesError

Union selected type `{selected}` is not a member of declared

Error message

Union selected type `{selected}` is not a member of declared union `{union}`

What it means

CtypesError::UnionSelectedTypeNotMember { selected, union } is raised when an InboundValue claims a union selected type that is not actually one of the declared union's members. The decoder checks the selection against the declared union descriptor and refuses values that name a foreign or renamed variant, protecting against schema skew and malformed payloads.

Source

Thrown at baml_language/crates/bridge_ctypes/src/error.rs:36

    MapEntryMissingKey,

    /// Carries only the input length, not the input itself — untrusted hex
    /// blobs can be up to the FFI decode cap (~67M chars), and embedding
    /// them in error messages bloats logs and exposes payload contents.
    #[error("Invalid bigint hex string ({len} bytes)")]
    InvalidBigint { len: usize },

    /// Carries only the input length for over-cap decimal type literals, so a
    /// hostile descriptor cannot amplify logs by echoing its full payload.
    #[error("Invalid decimal bigint literal ({len} bytes)")]
    InvalidBigintLiteral { len: usize },

    #[error(
        "Invalid InboundValue.value_type: a root union or optional does not identify one exact selected type"
    )]
    InvalidInboundValueTypeRootUnion,

    #[error("Union selected type `{selected}` is not a member of declared union `{union}`")]
    UnionSelectedTypeNotMember { selected: String, union: String },

    #[error("Internal error: {0}")]
    InternalError(String),
}

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Re-align the producer's schema with the bridge: regenerate bindings after any union member change.
  2. Check the exact member names (the error names `selected` and `union`) and fix the selected value or the descriptor.
  3. Upgrade both sides to matching versions so the union definition agrees.
  4. Validate selections against the declared union in the host before the FFI call.

Example fix

// before
selected = "Image"  # removed from union in new schema
send_inbound(union="Media", selected=selected)
// after
selected = "ImageUrl"  # current member of Media
send_inbound(union="Media", selected=selected)
Defensive patterns

Strategy: validation

Validate before calling

def ensure_union_member(declared_union: set[str], selected: str):
    if selected not in declared_union:
        raise ValueError(f"{selected} is not a member of the declared union")

Try / catch

try:
    val = bridge.decode_inbound(buf, len(buf))
except BridgeError as e:
    if "is not a member of declared union" in str(e):
        raise ValueError("schema skew: regenerate producer bindings for the union") from e
    raise

Prevention

When it happens

Trigger: Decoding an inbound value where value_type/selected names a type absent from the declared union's member list — e.g. after renaming or removing a union member, or crossing bridge/library version boundaries.

Common situations: BAML schema updated (member added/renamed/removed) but the producer still sends the old selected-type name; two libraries at different versions disagreeing on union members; typos in hand-built descriptors.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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