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

  1. Read the per-field error list (position.path: message) to find exactly which attributes failed.
  2. Convert inputs to plain primitives: pass Hashes/Arrays/Strings/numbers/booleans/nil instead of custom objects.
  3. For model objects, explicitly serialize: model.as_json or model.to_json with only JSON-safe attributes.
  4. Add or fix custom to_json/#as_json for types like BigDecimal/Time (require 'json/add/...' or map to iso8601).
  5. 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

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


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