SeleniumHQ/selenium · error · Error::SerializationError

#{name} expected an object on the wire, got #{json_payload.i

Error message

#{name} expected an object on the wire, got #{json_payload.inspect}

What it means

A BiDi serialization Record (a generated protocol type) expects the wire payload to be a JSON object (Ruby Hash). from_json raises Error::SerializationError when the inbound value is not a Hash (e.g. a bare string, number, or array arrived where an object was expected). It is a strict shape check on the response envelope.

Source

Thrown at rb/lib/selenium/webdriver/bidi/serialization/record.rb:88

          # @api private
          module Deserializer
            def new(**kwargs)
              # Start from what was passed so ::Data's constructor rejects an unknown key, then fill
              # each field with its value or UNSET (omitted), forcing fixed discriminators.
              attributes = kwargs.dup
              fields.each { |f| attributes[f.name] = fixed?(f) ? f.fixed : attributes.fetch(f.name, UNSET) }
              attributes[:extensions] = kwargs.fetch(:extensions, {}) if extensible?
              validate_values(attributes)
              construct(**attributes)
            end

            # Inbound: builds from the wire. A missing required field is omitted and warned (or
            # raised in strict mode, in +wire_value+); enum tokens are mapped back to symbols and an
            # unrecognized one raises (in +read+); an undeclared property is captured silently
            # (extensible) or warned and dropped (closed) — strict on shape, lenient on extras.
            def from_json(json_payload)
              unless json_payload.is_a?(::Hash)
                raise Error::SerializationError, "#{name} expected an object on the wire, got #{json_payload.inspect}"
              end

              attributes = fields.to_h do |f|
                [f.name, wire_value(f, json_payload)]
              end
              undeclared = extra(json_payload)
              if extensible?
                attributes[:extensions] = undeclared # the spec sanctions these extras; preserve them silently
              else
                warn_undeclared(undeclared) unless undeclared.empty?
              end
              construct(**attributes)
            end

            private

            # Checks each field's value: a required field cannot be omitted (UNSET), a non-nullable
            # field cannot be nil (nil is neither a value nor the UNSET omit-sentinel, so it would be

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Inspect the full BiDi response to see what non-object value arrived at the failing field.
  2. Update Selenium to a version whose generated schema matches your browser.
  3. Update/downgrade the browser to match the Selenium BiDi schema.
  4. Report the schema mismatch with the offending payload to the Selenium project.
Defensive patterns

Strategy: validation

Validate before calling

def expect_bidi_object!(payload)
  raise "expected BiDi object, got #{payload.class}" unless payload.is_a?(::Hash)
end

Type guard

def bidi_object?(payload)
  payload.is_a?(::Hash)
end

Try / catch

begin
  record = SomeRecord.from_json(payload)
rescue Selenium::WebDriver::Error::SerializationError => e
  warn "BiDi shape error: #{e.message}"
  raise
end

Prevention

When it happens

Trigger: The browser/grid returns a non-object value at a field typed as a Record: a bare string where a struct is expected, an array, or a primitive. Reached during inbound deserialization of any BiDi response that mismatches the schema's object shape.

Common situations: Browser BiDi implementation diverges from the schema Selenium generated against; a proxy/grid rewrites the response; version skew between Selenium and browser; a malformed server response.

Related errors


AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14). Data as JSON: /api/errors/d8aa9e91b406e8cb. Report an issue: GitHub.