teamcapybara/capybara · critical · LoadError
Capybara requires `puma` version 3.8.0 or higher, please upg
Error message
Capybara requires `puma` version 3.8.0 or higher, please upgrade `puma` or register and specify your own server block
What it means
Puma is installed but too old: Capybara's :puma server block builds and runs the Puma server itself (to avoid signal-handler side effects of the rack handler's #run), which requires the handler to expose a .config class method. Rack::Handler::Puma gained config in puma 3.8.0; older versions fail the respond_to?(:config) check and raise LoadError.
Source
Thrown at lib/capybara/registrations/servers.rb:34
end
options = { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options)
base_class::Handler::WEBrick.run(app, **options)
end
Capybara.register_server :puma do |app, port, host, **options| # rubocop:disable Metrics/BlockLength
begin
require 'rackup'
rescue LoadError # rubocop:disable Lint/SuppressedException
end
begin
require 'rack/handler/puma'
rescue LoadError
raise LoadError, 'Capybara is unable to load `puma` for its server, please add `puma` to your project or specify a different server via something like `Capybara.server = :webrick`.'
end
puma_rack_handler = defined?(Rackup::Handler::Puma) ? Rackup::Handler::Puma : Rack::Handler::Puma
unless puma_rack_handler.respond_to?(:config)
raise LoadError, 'Capybara requires `puma` version 3.8.0 or higher, please upgrade `puma` or register and specify your own server block'
end
# If we just run the Puma Rack handler it installs signal handlers which prevent us from being able to interrupt tests.
# Therefore construct and run the Server instance ourselves.
# puma_rack_handler.run(app, { Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false }.merge(options))
default_options = { Host: host, Port: port, Threads: '0:4', workers: 0, daemon: false }
options = default_options.merge(options)
conf = puma_rack_handler.config(app, options)
conf.clamp
puma_ver = Gem::Version.new(Puma::Const::PUMA_VERSION)
require_relative 'patches/puma_ssl' if Gem::Requirement.new('>=4.0.0', '< 4.1.0').satisfied_by?(puma_ver)
logger = (defined?(Puma::LogWriter) ? Puma::LogWriter : Puma::Events).then do |cls|
conf.options[:Silent] ? cls.strings : cls.stdio
end
conf.options[:log_writer] = loggerView on GitHub (pinned to 15b5fdb76e)
Solutions
- Upgrade puma to at least 3.8.0 (puma ~> 5.0 or ~> 6.0 recommended) and run bundle update puma.
- Check what is pinning it: bundle info puma and look at Gemfile.lock dependencies that constrain the version.
- If an upgrade is impossible, use a different server: Capybara.server = :webrick (add the webrick gem on Ruby 3+).
- Confirm the runtime version Capybara sees: require 'puma'; puts Puma::Const::PUMA_VERSION in the test boot.
Example fix
# before (Gemfile) gem 'puma', '~> 2.16' # after (Gemfile) gem 'puma', '~> 6.0'
Defensive patterns
Strategy: validation
Validate before calling
require 'puma'
raise LoadError, 'Capybara needs puma >= 3.8.0' if Gem::Version.new(Puma::Const::PUMA_VERSION) < Gem::Version.new('3.8.0') Type guard
def puma_config_capable? handler = defined?(Rackup::Handler::Puma) ? Rackup::Handler::Puma : Rack::Handler::Puma handler.respond_to?(:config) end
Prevention
- Constrain puma in the Gemfile (gem 'puma', '>= 3.8') so bundler fails at install time, not mid-spec-run.
- After environment changes, verify with `bundle exec ruby -e "puts Puma::Const::PUMA_VERSION"` in CI.
When it happens
Trigger: Gemfile.lock pins puma to < 3.8.0 (e.g. puma (~> 2.16) on a legacy Rails app) or a system/global old puma shadows the bundled one; the first spec that boots the Capybara server raises this.
Common situations: Legacy applications with old puma constraints; transitive gem pins preventing an upgrade; deploying test suite into an image that ships its own puma; upgrading Capybara without revisiting the puma constraint.
Related errors
- Capybara is unable to load `puma` for its server, please add
- Invalid option #{match.inspect} for :match, should be one of
- Invalid option(s) #{invalid_names}, should be one of #{valid
- #{@type} is not a valid type for a text query
- redirected more than #{driver.redirect_limit} times, check f
AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21).
Data as JSON: /api/errors/2b19d932ab5ebf46.
Report an issue: GitHub.