BoundaryML/baml · error · CtypesError
Invalid InboundValue.value_type: a root union or optional do
Error message
Invalid InboundValue.value_type: a root union or optional does not identify one exact selected type
What it means
CtypesError::InvalidInboundValueTypeRootUnion is thrown when decoding an InboundValue whose value_type identifies a root union (or optional) instead of exactly one selected concrete type. At the bridge boundary the decoder needs to know which single alternative was chosen; a root-level union/optional tag gives it nothing to reconstruct.
Source
Thrown at baml_language/crates/bridge_ctypes/src/error.rs:31
#[error("Invalid handle key: {0}")]
InvalidHandleKey(u64),
#[error("Map entry missing key")]
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
- Unwrap the union/optional in the host and send the selected concrete value with its exact type.
- If the value is Optional::None, send the dedicated null/none inbound encoding rather than a root optional tag.
- Update the producer SDK so unions encode a selected member plus its index/type id.
- Round-trip the payload through the decoder in tests to catch root-union encodings early.
Example fix
// before
value = InboundValue { value_type: UNION_T, ... } # root union
// after
value = InboundValue { value_type: SELECTED_MEMBER_T, union_index: Some(1), ... } Defensive patterns
Strategy: validation
Validate before calling
def ensure_selected_inbound(v):
if v.WhichOneof("value") in ("union", "optional") and v is root:
raise ValueError("root inbound value must be a selected concrete type, not a union/optional") Type guard
def is_root_union_or_optional(v) -> bool:
t = v.value_type
return t.is_union or t.is_optional Try / catch
try:
val = bridge.decode_inbound(buf, len(buf))
except BridgeError as e:
if "root union or optional" in str(e):
raise ValueError("unwrap the union/optional and send the selected member") from e
raise Prevention
- Always unwrap optionals and select a union member before serializing inbound args
- Encode Optional::None via the dedicated none encoding, not a root optional tag
- Add round-trip tests whenever a root argument type becomes a union
When it happens
Trigger: Encoding an inbound argument whose top-level type is a union or optional and sending it without selecting the concrete member — i.e. value_type set to a union/optional descriptor rather than the selected variant's type.
Common situations: Hand-built InboundValues in tools/tests where the caller forgot to unwrap an Optional; SDKs serializing an unselected union; schema changes making the root type a union while callers still send the old flat shape.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Protobuf decode error: {0}
- Map entry missing key
- Union selected type `{selected}` is not a member of declared
- Failed to decode Value
- Key is missing
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/abb69da46c72bdff.
Report an issue: GitHub.