{"record":{"id":"27a1e0066c1b2a63","repo":"bblimke/webmock","slug":"must-be-one-of-valid-types-but-you-ve-used-a","errorCode":null,"errorMessage":"must be one of: #{valid_types}, but you've used a #{@body.class}. Please convert it by calling .to_json .to_xml, or otherwise convert it to a string.","messagePattern":"must be one of: #(.+?), but you've used a #(.+?)\\. Please convert it by calling \\.to_json \\.to_xml, or otherwise convert it to a string\\.","errorType":"exception","errorClass":"WebMock::Response::InvalidBody","httpStatus":null,"severity":"error","filePath":"lib/webmock/response.rb","lineNumber":121,"sourceCode":"    end\n\n    private\n\n    def stringify_body!\n      if @body.is_a?(IO) || @body.is_a?(Pathname)\n        io = @body\n        @body = io.read\n        io.close if io.respond_to?(:close)\n      end\n    end\n\n    def assert_valid_body!\n      valid_types = [Proc, IO, Pathname, String, Array]\n      return if @body.nil?\n      return if valid_types.any? { |c| @body.is_a?(c) }\n\n      if @body.is_a?(Hash)\n        raise InvalidBody, \"must be one of: #{valid_types}, but you've used a #{@body.class}. \" \\\n          \"Please convert it by calling .to_json .to_xml, or otherwise convert it to a string.\"\n      else\n        raise InvalidBody, \"must be one of: #{valid_types}. '#{@body.class}' given.\"\n      end\n    end\n\n    def read_raw_response(io)\n      socket = ::Net::BufferedIO.new(io)\n      response = ::Net::HTTPResponse.read_new(socket)\n      transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl\n      response.reading_body(socket, true) {}\n\n      options = {}\n      options[:headers] = {}\n      response.each_header {|name, value| options[:headers][name] = value}\n      options[:headers]['transfer-encoding'] = transfer_encoding if transfer_encoding\n      options[:body] = response.read_body\n      options[:status] = [response.code.to_i, response.message]","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/bblimke/webmock/blob/b187df8827e1f08a1684a8ddc0a5050dbd449c16/lib/webmock/response.rb#L103-L139","documentation":"WebMock::Response#body= validates the body of a stubbed response: only Proc, IO, Pathname, String and Array (plus nil) are accepted. A Hash is deliberately rejected with this message from lib/webmock/response.rb:121 because webmock will not guess your serialization - a Hash is ambiguous between JSON, XML and form-encoding. Note the asymmetry that trips developers: on the request side with(body: {...}) accepts hashes and compares them structurally, but the response side requires an already-serialized string.","triggerScenarios":"stub_request(:any, url).to_return(body: { error: 'not found' }, status: 404). WebMock::Response.new(body: { ok: true }). A helper that builds a payload hash and passes it straight through as the response body.","commonSituations":"JSON API stubbing where the payload is naturally authored as a Ruby hash; refactoring specs from string bodies to structured ones; copy-pasting a with(body:) hash into to_return(body:).","solutions":["Serialize it yourself: .to_return(body: { error: 'not found' }.to_json, headers: { 'Content-Type' => 'application/json' })","Or switch to the JSON helper: .to_return_json(body: { error: 'not found' }) - it serializes and sets the content type","If the hash is actually form data, use .to_return(body: URI.encode_www_form(hash))"],"exampleFix":"# before\nstub_request(:get, 'https://api.example.com/me').to_return(body: { id: 1, name: 'Ada' })  # WebMock::InvalidBody\n\n# after\nstub_request(:get, 'https://api.example.com/me').to_return_json(body: { id: 1, name: 'Ada' })","handlingStrategy":"type-guard","validationCode":"payload = { id: 1 }\nstub.to_return(body: payload.respond_to?(:to_hash) ? payload.to_json : payload)","typeGuard":"def stub_body_valid?(b)\n  b.nil? || [Proc, IO, Pathname, String, Array].any? { |c| b.is_a?(c) }\nend","tryCatchPattern":"begin\n  stub.to_return(body: payload)\nrescue WebMock::InvalidBody\n  stub.to_return_json(body: payload)  # payload is a Hash: serialize it\nend","preventionTips":["Use to_return_json for hash payloads","Serialize in a payload factory so specs only ever see strings","Remember with(body:) accepts hashes but to_return(body:) does not"],"tags":["webmock","json","stub-response","serialization","ruby"],"backgroundTag":"invalid-response-body-type","analyzedSha":"b187df8827e1f08a1684a8ddc0a5050dbd449c16","analyzedAt":"2026-08-23T02:55:22.028Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}