{"record":{"id":"bad1aeeeb3ec75f6","repo":"BoundaryML/baml","slug":"serializing-completionstate-is-safe","errorCode":null,"errorMessage":"Serializing CompletionState is safe.","messagePattern":"Serializing CompletionState is safe\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"engine/language_client_ruby/ext/ruby_ffi/src/ruby_to_json.rs","lineNumber":79,"sourceCode":"        types: RModule,\n        partial_types: RModule,\n        allow_partials: bool,\n        mut from: ResponseBamlValue,\n    ) -> crate::Result<Value> {\n        let allow_partials = allow_partials && !from.0.meta().2.required_done;\n        // If we encounter a BamlValue node with check results, serialize it as\n        // { value: T, checks: K }. To compute `value`, we strip the metadata\n        // off the node and pass it back to `serialize_baml`.\n        let ResponseValueMeta(_flags, checks, completion, ..) = from.0.meta_mut();\n\n        if completion.display && allow_partials {\n            let hash = ruby.hash_new();\n            let stream_state_class = ruby.eval::<RClass>(\"Baml::StreamState\")?;\n            hash.aset(\n                ruby.sym_new(\"state\"),\n                ruby.sym_new(\n                    serde_json::to_string(&completion.state)\n                        .expect(\"Serializing CompletionState is safe.\"),\n                ),\n            )?;\n            completion.display = false;\n            let serialized_subvalue =\n                RubyToJson::serialize_baml(ruby, types, partial_types, allow_partials, from)?;\n            hash.aset(ruby.sym_new(\"value\"), serialized_subvalue)?;\n            let res = stream_state_class.funcall(\"new\", (hash,));\n            Ok(res?)\n        }\n        // Otherwise encode it directly.\n        else if !checks.is_empty() {\n            let serialized_checks = Self::serialize_response_checks(ruby, checks)?;\n\n            checks.clear();\n\n            let serialized_subvalue =\n                Self::serialize_baml(ruby, types, partial_types, allow_partials, from)?;\n","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/language_client_ruby/ext/ruby_ffi/src/ruby_to_json.rs#L61-L97","documentation":"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.","triggerScenarios":"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).","commonSituations":"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).","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."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"expected = Gem::Version.new(ENV.fetch('EXPECTED_BAML_VERSION'))\nactual = Gem::Specification.find_by_name('baml').version\nraise 'baml gem version mismatch; rebuild native ext' unless actual == expected","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["ruby","panic","serialization","streaming"],"backgroundTag":"internal-invariant-violation","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}