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 Union type with every arm being an object (object_only) cannot match a bare scalar payload. from_json returns bare scalars unchanged for normal unions, but for object_only unions a non-Hash payload raises Error::SerializationError because no variant could match.

Source

Thrown at rb/lib/selenium/webdriver/bidi/serialization/union.rb:63

            def fallback(path) = @fallback = path

            # Declared (via the schema's `objectOnly` signal) on a union whose every arm is an
            # object, so a non-Hash payload is a schema violation rather than a scalar arm.
            def object_only = @object_only = true

            # Declared (via the schema's `scalarValues` signal) on a non-object_only union whose
            # bare-scalar arms are a fixed set of literals (input.Origin's "viewport" / "pointer").
            # An outbound scalar outside that set matches no arm, so it is a caller error.
            def scalar_values(*values) = @scalar_values = values

            # A non-Hash payload is a bare scalar arm (e.g. input.Origin's "viewport") with no
            # object to dispatch on, so it is returned unchanged — unless every arm is an object
            # (object_only), where a non-Hash cannot match any variant and is a wire error.
            def from_json(json_payload)
              unless json_payload.is_a?(::Hash)
                return json_payload unless @object_only

                raise Error::SerializationError, "#{name} expected an object on the wire, got #{json_payload.inspect}"
              end

              variant = select(json_payload)
              unless variant
                raise Error::SerializationError,
                      "#{name} received a variant not in this Selenium's BiDi schema: #{json_payload.inspect}"
              end
              Protocol.const_get(variant).from_json(json_payload)
            end

            # Outbound mirror of from_json: build the variant the command's kwargs describe
            # so its typed as_json drives null-vs-absent per field (a flat hash through
            # Transport cannot). Dispatch keys are wire names equal to their ruby kwarg
            # (asserted at generation), so they match the kwargs by symbol. A mismatch here
            # is a caller error (unlike an unknown inbound value), so it fails loudly.
            def build(**kwargs)
              variant = outbound_variant(kwargs) ||
                        raise(::ArgumentError, "no #{name} variant matches #{kwargs.inspect}")

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Inspect the raw payload to confirm a non-object arrived at an object-only union.
  2. Verify the union is object_only in the current Selenium schema and the browser agrees.
  3. Align Selenium and browser/driver versions.
  4. Report the divergence with the payload.
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

begin
  value = SomeUnion.from_json(payload)
rescue Selenium::WebDriver::Error::SerializationError => e
  raise unless e.message =~ /expected an object on the wire/
  warn "object-only union got scalar: #{e.message}"
end

Prevention

When it happens

Trigger: Inbound BiDi payload at an object_only union field that is a non-Hash value (string, number, array, null-ish) — no object arm exists to dispatch to.

Common situations: Browser sends a bare scalar where the schema says every variant is an object, schema/client skew, non-conformant BiDi response.

Related errors


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