teamcapybara/capybara · error · TypeError

The second parameter to Session::new should be a rack app if

Error message

The second parameter to Session::new should be a rack app if passed.

What it means

The second parameter of Capybara::Session.new must be a Rack application - any object responding to #call - because the session's built-in server invokes it to serve requests. Passing anything else (notably a URL string) fails the respond_to?(:call) check with TypeError at construction time.

Source

Thrown at lib/capybara/session.rb:81

      execute_script evaluate_script evaluate_async_script visit refresh go_back go_forward send_keys
      within within_element within_fieldset within_table within_frame switch_to_frame
      current_window windows open_new_window switch_to_window within_window window_opened_by
      save_page save_and_open_page save_screenshot
      save_and_open_screenshot reset_session! response_headers
      status_code current_scope
      assert_current_path assert_no_current_path has_current_path? has_no_current_path?
    ].freeze + DOCUMENT_METHODS
    MODAL_METHODS = %i[
      accept_alert accept_confirm dismiss_confirm accept_prompt dismiss_prompt
    ].freeze
    DSL_METHODS = NODE_METHODS + SESSION_METHODS + MODAL_METHODS

    attr_reader :mode, :app, :server
    attr_accessor :synchronized

    def initialize(mode, app = nil)
      if app && !app.respond_to?(:call)
        raise TypeError, 'The second parameter to Session::new should be a rack app if passed.'
      end

      @@instance_created = true # rubocop:disable Style/ClassVars
      @mode = mode
      @app = app
      if block_given?
        raise 'A configuration block is only accepted when Capybara.threadsafe == true' unless Capybara.threadsafe

        yield config
      end
      @server = if config.run_server && @app && driver.needs_server?
        server_options = { port: config.server_port, host: config.server_host, reportable_errors: config.server_errors }
        server_options[:extra_middleware] = [Capybara::Server::AnimationDisabler] if config.disable_animation
        Capybara::Server.new(@app, **server_options).boot
      end
      @touched = false
    end

View on GitHub (pinned to 15b5fdb76e)

Solutions

  1. Pass the Rack app: Capybara::Session.new(:selenium, Rails.application)
  2. For an external/running server, omit the app and set Capybara.app_host = 'http://localhost:3000'
  3. Prefer the global DSL (Capybara.app=, Capybara.app_host=, visit/page) instead of constructing sessions manually

Example fix

# before
session = Capybara::Session.new(:selenium, 'http://localhost:3000')

# after
Capybara.app_host = 'http://localhost:3000'
session = Capybara::Session.new(:selenium)
Defensive patterns

Strategy: type-guard

Validate before calling

unless app.nil? || app.respond_to?(:call)
  raise TypeError, "#{app.inspect} is not a Rack app; set Capybara.app_host for external servers"
end
session = Capybara::Session.new(:selenium, app)

Type guard

def rack_app?(obj)
  obj.respond_to?(:call)
end

session = rack_app?(candidate) ? Capybara::Session.new(:selenium, candidate) : Capybara::Session.new(:selenium)

Try / catch

begin
  Capybara::Session.new(:selenium, app)
rescue TypeError
  Capybara.app_host = app.to_s # it was a URL string
  Capybara::Session.new(:selenium)
end

Prevention

When it happens

Trigger: Capybara::Session.new(:selenium, 'http://localhost:3000') - passing a URL string instead of an app; passing a route/path helper result or a PORO; passing MyApp instead of MyApp::Application in Rails.

Common situations: Trying to point a hand-built session at an already-running server (URLs belong in Capybara.app_host, not the app slot); migrating from tools whose constructors take a base URL; picking the wrong constant when both an app class and a routes object exist.

Related errors


AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21). Data as JSON: /api/errors/72279520de03753d. Report an issue: GitHub.