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

  1. Rebuild/reinstall the native extension: gem pristine baml or bundle update baml so Rust and Ruby halves match versions.
  2. Check for mixed baml gem versions in the bundle (bundle exec gem list baml) and remove duplicates.
  3. If reproducible on the latest version, file a BAML bug — this path is an internal invariant that should never fail.
  4. 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

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


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