lostisland/faraday · error · Faraday::Adapter::Test::Stubs::NotFound
no stubbed request for #{env[:method]} #{env[:url]} #{env[:b
Error message
no stubbed request for #{env[:method]} #{env[:url]} #{env[:body]} #{env[:headers]} What it means
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.
Source
Thrown at lib/faraday/adapter/test.rb:285
super(app)
@stubs = stubs || Stubs.new
configure(&block) if block
end
def configure
yield(stubs)
end
# @param env [Faraday::Env]
def call(env)
super
env.request.params_encoder ||= Faraday::Utils.default_params_encoder
env[:params] = env.params_encoder.decode(env[:url].query) || {}
stub, meta = stubs.match(env)
unless stub
raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \
"#{env[:url]} #{env[:body]} #{env[:headers]}"
end
block_arity = stub.block.arity
params = if block_arity >= 0
[env, meta].take(block_arity)
else
[env, meta]
end
timeout = request_timeout(:open, env[:request])
timeout ||= request_timeout(:read, env[:request])
status, headers, body =
if timeout
::Timeout.timeout(timeout, Faraday::TimeoutError) do
stub.block.call(*params)
endView on GitHub (pinned to b25b1b26cc)
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.
Example fix
# before
stubs = Faraday::Adapter::Test::Stubs.new
stubs.get('/api/users') { [200, {}, '[]'] }
conn = Faraday.new('https://api.example.com') do |b|
b.adapter :test, stubs
end
conn.get('/api/users/1') # => Stubs::NotFound
# after
stubs.get('/api/users/1') { [200, {}, '[{"id":1}]'] }
conn.get('/api/users/1') # => 200 Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
begin
conn.get('/api/users/1')
rescue Faraday::Adapter::Test::Stubs::NotFound => e
# e.message contains the unmatched method, URL, body and headers
flunk "unstubbed request — stub it or fix the path: #{e.message}"
end Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Expected :read, :write, :open. Got #{type.inspect} :(
- unknown http method: #{method}
- Can't convert #{params.class} into Hash.
- Can't convert #{params.class} into Hash.
- expected #{value_type.name} (got #{context[subkey].class.nam
AI-assisted analysis of lostisland/faraday@b25b1b26cc (2026-08-21).
Data as JSON: /api/errors/9ac1af68dca54fc1.
Report an issue: GitHub.