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
- Pass all three keys with positive integers (seconds): Rack::Attack::Fail2Ban.filter(req.ip, bantime: 3600, findtime: 600, maxretry: 5) { ... }.
- 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.
- Check for typos in the option keys - the API uses exactly :bantime, :findtime, :maxretry.
- 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
- Define the option trio once as a frozen constant and reuse it at every filter call site.
- Unit-test your initializer: expect Rack::Attack::Fail2Ban.filter('t', opts) { true } not to raise ArgumentError.
- Validate ENV-derived numbers with Integer(ENV.fetch('K', default)).
- Lint option keys in review - the names mirror the fail2ban daemon but as Ruby symbols.
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
- Must pass findtime option
- Must pass maxretry option
- Rack::Attack::MissingStoreError
- Must pass #{opt.inspect} option
AI-assisted analysis of rack/rack-attack@b771ea18af (2026-08-21).
Data as JSON: /api/errors/1c7e39fe558980a1.
Report an issue: GitHub.