bblimke/webmock · error · WebMock::Util::Parsers::ParseError
Invalid JSON string: #{yaml}, Error: #{e.inspect}
Error message
Invalid JSON string: #{yaml}, Error: #{e.inspect} What it means
WebMock::ParseError raised when WebMock's internal JSON parser (Util::Parsers::JSON, which converts JSON to YAML and calls YAML.load) cannot parse a body during request/stub body normalization. It fires when either the stub body pattern or the real request body is unparseable; the message echoes the offending string plus the underlying ArgumentError or Psych::SyntaxError. Because parsing routes through YAML, even valid JSON can fail when YAML misreads a scalar (classic case: unquoted date-like strings).
Source
Thrown at lib/webmock/util/parsers/json.rb:20
# This is a copy of https://github.com/jnunemaker/crack/blob/master/lib/crack/json.rb
# with date parsing removed
# Copyright (c) 2004-2008 David Heinemeier Hansson
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
require_relative "parse_error"
module WebMock
module Util
module Parsers
class JSON
def self.parse(json)
yaml = unescape(convert_json_to_yaml(json))
YAML.load(yaml)
rescue ArgumentError, Psych::SyntaxError => e
raise ParseError, "Invalid JSON string: #{yaml}, Error: #{e.inspect}"
end
protected
def self.unescape(str)
str.gsub(/\\u([0-9a-f]{4})/) { [$1.hex].pack("U") }
end
# Ensure that ":" and "," are always followed by a space
def self.convert_json_to_yaml(json) #:nodoc:
scanner, quoting, marks, times = StringScanner.new(json), false, [], []
while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
case char = scanner[1]
when '"', "'"
if !quoting
quoting = char
elsif quoting == char
quoting = falseView on GitHub (pinned to b187df8827)
Solutions
- Copy the body shown in the error message and run it through WebMock::Util::JSON.parse or JSON.parse in a console to see the exact cause
- Fix the malformed JSON: remove trailing commas, double-quote keys and strings, drop JavaScript-only literals (NaN, undefined, single quotes)
- If YAML scalar coercion is the culprit, match the raw body string instead of a parsed hash: .with(body: '{"d":"2026-08-22"}')
- For exotic bodies use a block matcher: .with(headers: { 'Content-Type' => 'application/json' }) { |req| JSON.parse(req.body)['d'] == '2026-08-22' }
- Upgrade webmock; parser edge cases (dates, unicode) keep getting fixed across releases
Example fix
// before
stub_request(:post, 'www.example.com')
.with(body: { 'd' => '2026-08-22' }, headers: { 'Content-Type' => 'application/json' })
# date-like scalar can break the YAML-backed parser on some psych versions
// after - match the raw JSON string exactly
stub_request(:post, 'www.example.com')
.with(body: '{"d":"2026-08-22"}', headers: { 'Content-Type' => 'application/json' }) Defensive patterns
Strategy: validation
Validate before calling
require 'json'
def valid_json_body?(body)
JSON.parse(body)
true
rescue JSON::ParserError
false
end
body = client.build_payload
raise ArgumentError, 'client produced invalid JSON' unless valid_json_body?(body)
HTTP.post(url, body: body, headers: { 'Content-Type' => 'application/json' }) Type guard
def stubbable_json_pattern?(pattern)
return true unless pattern.is_a?(String)
return true unless pattern.strip.start_with?('{', '[')
valid_json_body?(pattern)
end Try / catch
begin WebMock::Util::JSON.parse(body) rescue WebMock::ParseError => e # e.message shows the converted YAML plus the Psych/ArgumentError cause; # fix the payload or fixture - do not swallow this in tests end
Prevention
- Always build request bodies with JSON.generate / JSON.dump instead of string interpolation
- Run fixtures through JSON.parse once at load time so bad fixtures fail before stubbing
- Prefer exact string body matching for payloads with date-like or bare scalar values
- Re-check pinned psych/webmock versions after Ruby upgrades
When it happens
Trigger: stub_request(:post, url).with(body: {...}, headers: { 'Content-Type' => 'application/json' }) where either side contains invalid JSON: trailing commas, single-quoted strings, unquoted keys, NaN/Infinity; a JSON string body pattern like .with(body: '{"a":1,}'); YAML date-coercion failures on values such as 2026-08-22 under psych versions that turn them into Date objects; strings Psych rejects after the json-to-yaml conversion.
Common situations: Hand-written JSON fixtures with trailing commas; payloads assembled by string interpolation instead of a JSON serializer; Ruby or psych upgrades making YAML.load stricter or changing date coercion; non-UTF-8 bytes or a BOM in the body; tests that stub with a Hash while the client sends malformed JSON.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- #to_return_json does not support passing a block
- must be one of: #{valid_types}, but you've used a #{@body.cl
- Invalid XML string: #{xml}, Error: #{e.inspect}
- Real HTTP connections are disabled. Unregistered request: #{
- #with method invoked with no arguments. Either options hash
AI-assisted analysis of bblimke/webmock@b187df8827 (2026-08-23).
Data as JSON: /api/errors/25e4cf8f1952bfce.
Report an issue: GitHub.