mislav/will_paginate · error · RangeError

invalid #{name}: #{value.inspect}

Error message

invalid #{name}: #{value.inspect}

What it means

WillPaginate::PageNumber coerces its input with Integer(value) and enforces a range: page numbers must be >= 1, offsets must be 0..BIGINT (2^63-1, the SQL bigint ceiling). Violations raise RangeError 'invalid page/offset: ...', and any coercion failure (ArgumentError/TypeError) is extended with the WillPaginate::InvalidPage module before re-raising, so all bad-input cases are catchable as WillPaginate::InvalidPage.

Source

Thrown at lib/will_paginate/page_number.rb:17

require 'forwardable'

module WillPaginate
  # a module that page number exceptions are tagged with
  module InvalidPage; end

  # integer representing a page number
  class PageNumber < Numeric
    # a value larger than this is not supported in SQL queries
    BIGINT = 9223372036854775807

    extend Forwardable

    def initialize(value, name)
      value = Integer(value)
      if 'offset' == name ? (value < 0 or value > BIGINT) : value < 1
        raise RangeError, "invalid #{name}: #{value.inspect}"
      end
      @name = name
      @value = value
    rescue ArgumentError, TypeError, RangeError => error
      error.extend InvalidPage
      raise error
    end

    def to_i
      @value
    end

    def_delegators :@value, :coerce, :==, :<=>, :to_s, :+, :-, :*, :/, :to_json

    def inspect
      "#{@name} #{to_i}"
    end

View on GitHub (pinned to 50017c3eb0)

Solutions

  1. Sanitize the param at the boundary: page = params[:page].presence && params[:page].to_i; page = nil if page && page < 1 — nil coerces to page 1 downstream
  2. Rescue WillPaginate::InvalidPage in the controller and fall back to page 1 or render a 404
  3. Clamp derived offsets to 0..WillPaginate::PageNumber::BIGINT before passing them to the offset API

Example fix

// before
@posts = Post.page(params[:page])

// after
raw = params[:page].to_s
page = raw.match?(/\A[1-9]\d*\z/) ? raw.to_i : 1
@posts = Post.page(page)
Defensive patterns

Strategy: try-catch

Validate before calling

raw = params[:page].to_s
page = raw.match?(/\A[1-9]\d*\z/) ? raw.to_i : nil # nil -> page 1 downstream
page = nil if page && page > WillPaginate::PageNumber::BIGINT
@posts = Post.page(page)

Type guard

def valid_page_number?(value)
  s = value.to_s
  s.match?(/\A[1-9]\d*\z/) && s.to_i <= WillPaginate::PageNumber::BIGINT
end

Try / catch

begin
  @posts = Post.page(params[:page]).per(10)
rescue WillPaginate::InvalidPage # RangeError/ArgumentError/TypeError tagged by PageNumber
  redirect_to(posts_path(page: 1)) and return # or render plain: 'Not Found', status: 404
end

Prevention

When it happens

Trigger: Model.page(params[:page]) with params[:page] = 'abc' (Integer('abc') -> ArgumentError), '0' or 0 or -3 (below the minimum of 1), or a URL-crafted huge value like 99999999999999999999 (Integer works but exceeds nothing... only offsets compare to BIGINT; a page that huge is fine for page, but offset = (page-1)*per_page can exceed BIGINT and blow up in the offset branch). Also .page(1.5) style garbage from form input.

Common situations: Public-facing list endpoints where :page comes straight from the query string and is never sanitized; scrapers/bots probing ?page=0 or ?page=-1; user-typed junk in a search box bound to the page param; wrapping arithmetic that computes an offset overflow.

Related errors


AI-assisted analysis of mislav/will_paginate@50017c3eb0 (2026-08-21). Data as JSON: /api/errors/3d2ac631f0bd3ea3. Report an issue: GitHub.