{"record":{"id":"9ac1af68dca54fc1","repo":"lostisland/faraday","slug":"no-stubbed-request-for-env-method-env-url","errorCode":null,"errorMessage":"no stubbed request for #{env[:method]} #{env[:url]} #{env[:body]} #{env[:headers]}","messagePattern":"no stubbed request for #(.+?) #(.+?) #(.+?) #(.+?)","errorType":"exception","errorClass":"Faraday::Adapter::Test::Stubs::NotFound","httpStatus":null,"severity":"error","filePath":"lib/faraday/adapter/test.rb","lineNumber":285,"sourceCode":"        super(app)\n        @stubs = stubs || Stubs.new\n        configure(&block) if block\n      end\n\n      def configure\n        yield(stubs)\n      end\n\n      # @param env [Faraday::Env]\n      def call(env)\n        super\n\n        env.request.params_encoder ||= Faraday::Utils.default_params_encoder\n        env[:params] = env.params_encoder.decode(env[:url].query) || {}\n        stub, meta = stubs.match(env)\n\n        unless stub\n          raise Stubs::NotFound, \"no stubbed request for #{env[:method]} \" \\\n                                 \"#{env[:url]} #{env[:body]} #{env[:headers]}\"\n        end\n\n        block_arity = stub.block.arity\n        params = if block_arity >= 0\n                   [env, meta].take(block_arity)\n                 else\n                   [env, meta]\n                 end\n\n        timeout = request_timeout(:open, env[:request])\n        timeout ||= request_timeout(:read, env[:request])\n\n        status, headers, body =\n          if timeout\n            ::Timeout.timeout(timeout, Faraday::TimeoutError) do\n              stub.block.call(*params)\n            end","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/lostisland/faraday/blob/b25b1b26ccef34b1460b0267115be238ca758087/lib/faraday/adapter/test.rb#L267-L303","documentation":"Faraday's test adapter replaces real HTTP with stubs registered on a Faraday::Adapter::Test::Stubs object. On every request the adapter calls stubs.match(env); when no registered stub matches the request (method, host, path, query, and under strict_mode also headers and body), it raises Stubs::NotFound. The message deliberately echoes the exact method, URL, body and headers of the unmatched request so you can diff it against your stub declarations.","triggerScenarios":"Declaring stubs.get('/api/users') but requesting '/api/users/1'; stubbing a path on a different host than the connection's url_prefix; enabling stubs.strict_mode = true so a default User-Agent or Content-Type header no longer matches; a middleware (retry, token refresh, redirect follow) issuing an extra HTTP call you never stubbed; requesting a verb (:post) when only :get was stubbed; query params differing (?page=2 vs stubbed ?page=1).","commonSituations":"RSpec/Minitest suites using b.adapter :test, stubs; app code changes a path or adds a query param but the test stubs are not updated; strict_mode enabled to catch sloppy stubs which then flags harmless header differences; OAuth client middleware fetching a token in the background during a stubbed request; parallel test runs sharing stubs non-atomically.","solutions":["Read the error message: it prints the actual method, URL, body and headers of the request that failed to match, and compare them field-by-field with your stub declaration.","Add or correct the stub for that exact request: stubs.get('https://api.example.com/api/users/1') { ... } — remember the stub path must include the host when the connection requests an absolute URL.","If only headers or body differ, disable strict matching: stubs.strict_mode = false (the default) so stubs match on method/path/query only.","Stub the auxiliary request your middleware makes (e.g. the token endpoint), or disable that middleware in the test setup.","Call stubs.verify_stubbed_calls at the end of tests to also catch stubs that were never hit, keeping expectations two-way."],"exampleFix":"# before\nstubs = Faraday::Adapter::Test::Stubs.new\nstubs.get('/api/users') { [200, {}, '[]'] }\nconn = Faraday.new('https://api.example.com') do |b|\n  b.adapter :test, stubs\nend\nconn.get('/api/users/1') # => Stubs::NotFound\n\n# after\nstubs.get('/api/users/1') { [200, {}, '[{\"id\":1}]'] }\nconn.get('/api/users/1') # => 200","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"begin\n  conn.get('/api/users/1')\nrescue Faraday::Adapter::Test::Stubs::NotFound => e\n  # e.message contains the unmatched method, URL, body and headers\n  flunk \"unstubbed request — stub it or fix the path: #{e.message}\"\nend","preventionTips":["Print the error message verbatim in test output; it shows the exact method/URL/body/headers to stub.","Enable stubs.strict_mode in a dedicated strict spec run only, so header drift fails one suite instead of all of them.","Call stubs.verify_stubbed_call(s) after each test to keep stubs and real requests two-way consistent.","Create fresh Stubs per example instead of sharing them across parallel tests."],"tags":["ruby","faraday","test-adapter","stubs","mocking","rspec","testing"],"backgroundTag":"mock-stub-not-matched","analyzedSha":"b25b1b26ccef34b1460b0267115be238ca758087","analyzedAt":"2026-08-21T19:43:20.220Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}