rack/rack-attack · error · ArgumentError

Must pass bantime option

Error message

Must pass bantime option

What it means

Fail2Ban.filter(discriminator, options) requires three options: bantime (how long the ban lasts), findtime (counting window) and maxretry (failures before banning). The first line extracts bantime with 'or raise ArgumentError', so the error fires when options[:bantime] is missing - or explicitly nil/false. It is a fail-fast guard because a ban without a duration is meaningless.

Source

Thrown at lib/rack/attack/fail2ban.rb:8

# frozen_string_literal: true

module Rack
  class Attack
    class Fail2Ban
      class << self
        def filter(discriminator, options)
          bantime   = options[:bantime]   or raise ArgumentError, "Must pass bantime option"
          findtime  = options[:findtime]  or raise ArgumentError, "Must pass findtime option"
          maxretry  = options[:maxretry]  or raise ArgumentError, "Must pass maxretry option"

          if banned?(discriminator)
            # Return true for blocklist
            true
          elsif yield
            fail!(discriminator, bantime, findtime, maxretry)
          end
        end

        def reset(discriminator, options)
          findtime = options[:findtime] or raise ArgumentError, "Must pass findtime option"
          cache.reset_count("#{key_prefix}:count:#{discriminator}", findtime)
          # Clear ban flag just in case it's there
          cache.delete("#{key_prefix}:ban:#{discriminator}")
        end

View on GitHub (pinned to b771ea18af)

Solutions

  1. Pass all three keys with positive integers (seconds): Rack::Attack::Fail2Ban.filter(req.ip, bantime: 3600, findtime: 600, maxretry: 5) { ... }.
  2. If building options from a hash/config object, merge defaults: opts = {bantime: 3600, findtime: 600, maxretry: 5}.merge(user_opts) so keys can never be absent.
  3. Check for typos in the option keys - the API uses exactly :bantime, :findtime, :maxretry.
  4. Guard ENV-derived values: fail2ban opts like bantime: ENV['BANTIME']&.to_i will be nil when unset - fall back to a literal default.

Example fix

# before
Rack::Attack.blocklist('login abusers') do |req|
  Rack::Attack::Fail2Ban.filter(req.ip, findtime: 60, maxretry: 3) { req.path == '/login' && req.post? }
end
# => ArgumentError: Must pass bantime option

# after
Rack::Attack.blocklist('login abusers') do |req|
  Rack::Attack::Fail2Ban.filter(req.ip, bantime: 3600, findtime: 60, maxretry: 3) { req.path == '/login' && req.post? }
end
Defensive patterns

Strategy: validation

Validate before calling

FAIL2BAN_KEYS = %i[bantime findtime maxretry].freeze

opts = { bantime: 3600, findtime: 600, maxretry: 5 }.merge(user_opts)
missing = FAIL2BAN_KEYS.reject { |k| opts[k] }
raise ArgumentError, "fail2ban config missing: #{missing.join(', ')}" unless missing.empty?

Rack::Attack::Fail2Ban.filter(ip, opts) { yield_filter }

Type guard

def valid_fail2ban_options?(opts)
  %i[bantime findtime maxretry].all? { |k| opts[k] && opts[k].is_a?(Integer) && opts[k].positive? }
end

Try / catch

begin
  Rack::Attack::Fail2Ban.filter(ip, opts) { bad?(req) }
rescue ArgumentError => e
  raise unless e.message.start_with?('Must pass')
  ErrorReporter.report(e)
  false # config bug: do not block the client
end

Prevention

When it happens

Trigger: Calling Rack::Attack::Fail2Ban.filter(ip, findtime: 60, maxretry: 3) { ... } (bantime omitted), or passing bantime: nil / a misspelled key like ban_time:, inside a Rack::Attack.blocklist block - so it raises on the first request that evaluates the blocklist. Also triggered by direct calls such as Rack::Attack::Fail2Ban.filter('1.2.3.4', {}) { true }.

Common situations: Copy-pasting a Fail2Ban example and dropping the bantime line; renaming keys (bantime vs ban_time, or seconds vs ActiveSupport::Duration mistakes like passing 1.hour to a config expecting ints is fine, but passing nil from ENV parsing is not); constructing options dynamically (options = {findtime: x, maxretry: y} then forgetting bantime); earlier rack-attack versions not enforcing all keys, so an upgrade surfaces old configs.

Related errors


AI-assisted analysis of rack/rack-attack@b771ea18af (2026-08-21). Data as JSON: /api/errors/1c7e39fe558980a1. Report an issue: GitHub.