SeleniumHQ/selenium · error · Error::SerializationError

#{name}##{field.name} received null but is not nullable

Error message

#{name}##{field.name} received null but is not nullable

What it means

During BiDi Record deserialization, read() sees an explicit null (raw.nil?) for a field the schema does not mark nullable, and raises Error::SerializationError. Unlike a missing field, an explicit JSON null at a non-nullable position is a hard wire error in both lenient and strict modes.

Source

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

            end

            # A required field absent from the response is tolerated as omitted (UNSET) and warned, so a
            # schema ahead of the browser does not block the caller; strict mode (SE_BIDI_STRICT) escalates
            # to an error for callers who want it. Omitted (UNSET) stays distinct from an explicit null (nil),
            # which matters for the required-and-nullable fields the schema flags.
            def missing_required(field)
              message = "#{name}##{field.name} is required but was missing from the response"
              raise Error::SerializationError, message if Serialization.strict?

              WebDriver.logger.warn(message, id: :bidi_missing_required)
              UNSET
            end

            def read(field, raw)
              if raw.nil?
                return raw if field.nullable

                raise Error::SerializationError, "#{name}##{field.name} received null but is not nullable"
              end
              check_shape(field, raw)
              return Serialization.to_symbol("#{name}##{field.name}", raw, enum_hash(field)) if field.enum

              if field.ref.nil?
                check_primitive(field, raw) unless field.list
                return raw
              end

              read_ref(field, raw)
            end

            # 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)

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Capture the raw BiDi response to confirm which field arrived null.
  2. Verify the field is genuinely required/non-nullable in the W3C BiDi spec for your browser version.
  3. Align Selenium and browser/driver versions.
  4. File a bug against the browser or Selenium with the payload if the spec disagrees with the behavior.
Defensive patterns

Strategy: try-catch

Validate before calling

# Before trusting a BiDi value, coerce/validate non-nullability at the call site.
def non_null!(value, field)
  raise "#{field} must not be null" if value.nil?
  value
end

Type guard

def present?(value)
  !value.nil?
end

Try / catch

begin
  record = SomeRecord.from_json(payload)
rescue Selenium::WebDriver::Error::SerializationError => e
  raise unless e.message =~ /received null but is not nullable/
  warn "non-nullable field arrived null: #{e.message}"
end

Prevention

When it happens

Trigger: The browser/grid sends JSON null at a BiDi field declared non-nullable by the schema. Reached on inbound from_json for any Record field that is not flagged nullable.

Common situations: Browser returns null where the spec requires a value, server bug, version skew, proxy rewriting a value to null, nullable flag mis-generated in the schema.

Related errors


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