bblimke/webmock · error · WebMock::Util::Parsers::ParseError

Invalid XML string: #{xml}, Error: #{e.inspect}

Error message

Invalid XML string: #{xml}, Error: #{e.inspect}

What it means

WebMock::ParseError raised when Crack::XML cannot parse an XML request body during body normalization for matching; the underlying REXML::ParseException is wrapped. It fires when a stub declares an XML body pattern (Hash or String) and either that pattern or the incoming request body is not well-formed XML. The message echoes the offending document and the parser cause.

Source

Thrown at lib/webmock/util/parsers/xml.rb:11

require_relative "parse_error"
require "crack/xml"

module WebMock
  module Util
    module Parsers
      class XML
        def self.parse(xml)
          ::Crack::XML.parse(xml)
        rescue ::REXML::ParseException => e
          raise ParseError, "Invalid XML string: #{xml}, Error: #{e.inspect}"
        end
      end
    end
  end
end

View on GitHub (pinned to b187df8827)

Solutions

  1. Parse the document shown in the error with REXML::Document or Nokogiri to get the exact line/column of the break
  2. Fix well-formedness: one root element, all tags closed, ampersands escaped as &, prolog matching the actual encoding
  3. Replace undefined HTML entities or supply the DOCTYPE that defines them
  4. Match the body as an exact String instead of a parsed XML Hash when byte-exact matching is acceptable

Example fix

// before
stub_request(:post, 'www.example.com')
  .with(body: '<a>1</a><b>2</b>', headers: { 'Content-Type' => 'application/xml' })
# two root elements -> REXML::ParseException -> WebMock::ParseError

// after
stub_request(:post, 'www.example.com')
  .with(body: '<r><a>1</a><b>2</b></r>', headers: { 'Content-Type' => 'application/xml' })
Defensive patterns

Strategy: validation

Validate before calling

require 'rexml/document'

def well_formed_xml?(body)
  REXML::Document.new(body)
  true
rescue REXML::ParseException
  false
end

xml = client.build_xml
raise ArgumentError, 'client produced malformed XML' unless well_formed_xml?(xml)
HTTP.post(url, body: xml, headers: { 'Content-Type' => 'application/xml' })

Try / catch

begin
  WebMock::Util::XML.parse(body)
rescue WebMock::ParseError => e
  # e.message contains the document and the REXML cause - fix the XML, never rescue-and-ignore in tests
end

Prevention

When it happens

Trigger: stub_request(:post, url).with(body: xml_or_hash, headers: { 'Content-Type' => 'application/xml' }) where the XML has unclosed tags, more than one root element, undefined entities like &nbsp;, a broken encoding declaration, or is truncated (e.g. a streaming client cut off mid-payload).

Common situations: Fixtures containing HTML entities that are undefined in XML; XML assembled by concatenating fragments; HTML sent with an XML content type; encodings declared in the prolog that disagree with the actual bytes; clients that truncate bodies on timeout.

Related errors


AI-assisted analysis of bblimke/webmock@b187df8827 (2026-08-23). Data as JSON: /api/errors/48e7590467d996a3. Report an issue: GitHub.