BoundaryML/baml · critical
Serializing CompletionState is safe.
Error message
Serializing CompletionState is safe.
What it means
An expect() panic message (not a recoverable Ruby error) inside serialize_baml when serializing a BAML StreamState: serde_json::to_string(&completion.state) is asserted to always succeed. If this ever fails it panics the Rust FFI thread, typically surfacing in Ruby as a crashes/abort rather than a catchable exception. The message documents the invariant that CompletionState is always JSON-serializable.
Source
Thrown at engine/language_client_ruby/ext/ruby_ffi/src/ruby_to_json.rs:79
types: RModule,
partial_types: RModule,
allow_partials: bool,
mut from: ResponseBamlValue,
) -> crate::Result<Value> {
let allow_partials = allow_partials && !from.0.meta().2.required_done;
// If we encounter a BamlValue node with check results, serialize it as
// { value: T, checks: K }. To compute `value`, we strip the metadata
// off the node and pass it back to `serialize_baml`.
let ResponseValueMeta(_flags, checks, completion, ..) = from.0.meta_mut();
if completion.display && allow_partials {
let hash = ruby.hash_new();
let stream_state_class = ruby.eval::<RClass>("Baml::StreamState")?;
hash.aset(
ruby.sym_new("state"),
ruby.sym_new(
serde_json::to_string(&completion.state)
.expect("Serializing CompletionState is safe."),
),
)?;
completion.display = false;
let serialized_subvalue =
RubyToJson::serialize_baml(ruby, types, partial_types, allow_partials, from)?;
hash.aset(ruby.sym_new("value"), serialized_subvalue)?;
let res = stream_state_class.funcall("new", (hash,));
Ok(res?)
}
// Otherwise encode it directly.
else if !checks.is_empty() {
let serialized_checks = Self::serialize_response_checks(ruby, checks)?;
checks.clear();
let serialized_subvalue =
Self::serialize_baml(ruby, types, partial_types, allow_partials, from)?;
View on GitHub (pinned to bd85ce9dee)
Solutions
- Rebuild/reinstall the native extension: gem pristine baml or bundle update baml so Rust and Ruby halves match versions.
- Check for mixed baml gem versions in the bundle (bundle exec gem list baml) and remove duplicates.
- If reproducible on the latest version, file a BAML bug — this path is an internal invariant that should never fail.
- As a workaround, disable streaming/partial usage until the extension is fixed.
Defensive patterns
Strategy: fallback
Validate before calling
expected = Gem::Version.new(ENV.fetch('EXPECTED_BAML_VERSION'))
actual = Gem::Specification.find_by_name('baml').version
raise 'baml gem version mismatch; rebuild native ext' unless actual == expected Prevention
- Always reinstall/rebuild the native extension after upgrading the gem (gem pristine baml).
- Pin a single baml gem version in the Gemfile.lock.
- Treat this panic as a bug: file an issue with repro steps if it occurs on latest.
- Avoid mixing streaming responses across different baml versions in long-lived processes.
When it happens
Trigger: Calling serialize_baml on a completion whose state is a BAML StreamState — i.e. any streaming/partial function call path — where the internal CompletionState enum unexpectedly fails serde_json serialization (would indicate an internal invariant violation or an incompatible baml_types version mismatch).
Common situations: Mixing mismatched versions of the baml Ruby gem and compiled ruby_ffi extension so CompletionState serde derives disagree; native extension stale after a gem upgrade (requires recompilation).
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- error while parsing stream_function args: {e}
- PackageInterface artifact serialization into Vec is infallib
- realized union-member template failed to substitute: {e}
- unexpected stream state
- {e:?}
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/bad1aeeeb3ec75f6.
Report an issue: GitHub.