SeleniumHQ/selenium · error · Error::SerializationError

#{name}##{field.name} expected #{field.list ? 'a list' : 'a

Error message

#{name}##{field.name} expected #{field.list ? 'a list' : 'a single value'}, got #{raw.inspect}

What it means

check_shape validates the JSON container shape of a BiDi field: a field declared as a list must arrive as a JSON array, and a non-list scalar/enum/ref field must NOT arrive as an array. A mismatch raises Error::SerializationError. Opaque fields (no shape descriptor) pass through unchecked.

Source

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

            # Reads a ref-typed value into its class. A `scalar` position is an inline union with
            # a scalar arm collapsed onto its union ref (a map's string keys): a non-object leaf
            # passes through instead of being handed to the object_only union, but only when it
            # matches the arm's primitive (+scalar+ carries it). A list recurses per element.
            def read_ref(field, raw)
              klass = (@refs ||= {})[field.name] ||= Protocol.const_get(field.ref)
              return read_list(field, raw, klass) if field.list

              field.scalar && !raw.is_a?(::Hash) ? scalar_value(field, raw) : klass.from_json(raw)
            end

            # A declared list must arrive as an array; a scalar-shaped field (enum or ref, not a
            # list) must not. An opaque field carries no shape descriptor, so it passes through.
            def check_shape(field, raw)
              return if field.list == raw.is_a?(::Array)
              return unless field.list || field.enum || field.ref

              raise Error::SerializationError,
                    "#{name}##{field.name} expected #{field.list ? 'a list' : 'a single value'}, got #{raw.inspect}"
            end

            # Ruby classes a checkable primitive admits. `number` is any Numeric (JSON has one
            # number type); `integer` requires an Integer — a browser emits `5`, not `5.0`, for an
            # integer (JS has no int/float split), so this rarely false-positives yet still rejects
            # a genuine non-integer like 1.5. A field with no primitive descriptor is left unchecked.
            PRIMITIVE_TYPES = {
              'string' => [::String], 'boolean' => [::TrueClass, ::FalseClass],
              'number' => [::Numeric], 'integer' => [::Integer]
            }.freeze

            def check_primitive(field, raw)
              expected = PRIMITIVE_TYPES[field.primitive]
              return if expected.nil? || expected.any? { |type| raw.is_a?(type) }

              raise Error::SerializationError, "#{name}##{field.name} expected #{field.primitive}, got #{raw.inspect}"
            end

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Log the raw payload to see whether an array or scalar arrived at the failing field.
  2. Confirm the field's list/scalar cardinality in the W3C BiDi spec for your browser version.
  3. Align Selenium client and browser versions.
  4. Report the divergence with the payload.
Defensive patterns

Strategy: validation

Validate before calling

# Confirm the cardinality your code expects matches the inbound container.
def expect_list!(value, field)
  raise "#{field} expected a list" unless value.is_a?(::Array)
  value
end

Type guard

def bidi_list?(value)
  value.is_a?(::Array)
end

Try / catch

begin
  record = SomeRecord.from_json(payload)
rescue Selenium::WebDriver::Error::SerializationError => e
  raise unless e.message =~ /expected.*list|expected a single value/
  warn "cardinality mismatch: #{e.message}"
end

Prevention

When it happens

Trigger: Browser sends a JSON array for a field the schema types as a single value, or a single value/object for a field typed as a list, during inbound BiDi Record deserialization.

Common situations: Schema/browser version skew where one side treats a field as singular and the other as a list, grid rewriting arrays, spec evolution changing cardinality.

Related errors


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