BoundaryML/baml · error · TypeError
failed to convert Ruby object to JSON, errors were: {} Ruby
Error message
failed to convert Ruby object to JSON, errors were:
{}
Ruby object:
{} What it means
A Ruby TypeError raised by RubyToJson::convert when a Ruby object passed across the FFI cannot be converted to JSON. It aggregates every field-level conversion error (position.path and message) plus the Ruby object's inspect() output. This is the entry-point variant used for whole Ruby values handed to BAML (e.g. function inputs).
Source
Thrown at engine/language_client_ruby/ext/ruby_ffi/src/ruby_to_json.rs:228
/// Convert a Ruby object to a JSON object.
///
/// We have to implement this ourselves instead of relying on Serde, because in the codegen,
/// we can't convert a BAML-generated type to a hash trivially (specifically union-typed
/// fields do not serialize correctly, see https://sorbet.org/docs/tstruct#serialize-gotchas)
///
/// We do still rely on :serialize for enums though.
pub fn convert(from: Value) -> crate::Result<BamlValue> {
let ruby = Ruby::get_with(from);
let result = RubyToJson { ruby: &ruby }.to_json(from, vec![]);
match result {
Ok(value) => Ok(value),
Err(e) => {
let mut errors = vec![];
for error in e {
errors.push(format!(" {}: {}", error.position.join("."), error.message));
}
Err(Error::new(
ruby.exception_type_error(),
format!(
"failed to convert Ruby object to JSON, errors were:\n{}\nRuby object:\n{}",
errors.join("\n"),
from.inspect()
),
))
}
}
}
pub fn convert_hash_to_json(from: RHash) -> crate::Result<IndexMap<String, BamlValue>> {
let ruby = Ruby::get_with(from);
let result = RubyToJson { ruby: &ruby }.hash_to_map(from, vec![]);
match result {
Ok(value) => Ok(value),
Err(e) => {View on GitHub (pinned to bd85ce9dee)
Solutions
- Read the per-field error list (position.path: message) to find exactly which attributes failed.
- Convert inputs to plain primitives: pass Hashes/Arrays/Strings/numbers/booleans/nil instead of custom objects.
- For model objects, explicitly serialize: model.as_json or model.to_json with only JSON-safe attributes.
- Add or fix custom to_json/#as_json for types like BigDecimal/Time (require 'json/add/...' or map to iso8601).
- Verify argument names/types match the .baml function signature after regenerating the client.
Example fix
// before
b.ExtractResume(resume: ResumeRecord.new(raw: raw_blob))
// after
b.ExtractResume(resume: {
name: record.name,
skills: record.skills.map(&:to_s),
created_at: record.created_at.iso8601
}) Defensive patterns
Strategy: validation
Validate before calling
def baml_safe?(obj) JSON.generate(obj) true rescue JSON::GeneratorError, TypeError false end # before calling: raise 'inputs not JSON-safe' unless baml_safe?(inputs)
Type guard
def json_primitive?(v)
v.nil? || v == true || v == false || v.is_a?(String) ||
v.is_a?(Integer) || v.is_a?(Float) ||
(v.is_a?(Array) && v.all? { |e| json_primitive?(e) }) ||
(v.is_a?(Hash) && v.values.all? { |e| json_primitive?(e) })
end Try / catch
begin
b.ExtractResume(resume: inputs)
rescue TypeError => e
logger.error("BAML input conversion failed: #{e.message}")
inputs = JSON.parse(inputs.to_json) # sanitize and retry
end Prevention
- Pass plain Hashes/Arrays/primitives to BAML functions, not model objects.
- Explicitly serialize Time/BigDecimal (iso8601, to_f) before passing.
- Round-trip complex inputs through JSON.parse(obj.to_json) to sanitize.
- Keep BAML parameter types and Ruby payloads in sync via codegen.
When it happens
Trigger: Passing a Ruby object as a BAML function argument (via parse_llm_response/invoke paths that call RubyToJson::convert) where some attribute is not JSON-serializable: symbols, BigDecimal without to_json, Time without a serializer, unexpected nested types, or objects not matching the declared parameter schema.
Common situations: Forgetting `require 'json'` or an ActiveModel serializer, passing ActiveRecord objects with non-serializable attributes (binary columns, symbols), passing a Ruby Struct/OpenStruct whose fields don't match the .baml parameter types, or passing core Ruby objects like Regexp/Range.
Understand the failure class
Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.
Related errors
- error while parsing call_function args: {e}
- failed coercing BAML value to Ruby value: {e:?}
- {e:?}
- Failed deserializing HTTP body as JSON: {e:?}
- {e:?}
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/a4c1e4c3d8f8b989.
Report an issue: GitHub.