mislav/will_paginate · error · ArgumentError
The #{collection_name} variable appears to be empty. Did you
Error message
The #{collection_name} variable appears to be empty. Did you forget to pass the collection object for will_paginate? What it means
The ActionView wrapper for will_paginate lets you call <%= will_paginate %> with no argument; it then infers the collection from the instance variable named after the controller ("@#{controller.controller_name}"). If that ivar is nil it raises ArgumentError telling you the variable 'appears to be empty' — meaning it was never assigned, not that the list has zero rows.
Source
Thrown at lib/will_paginate/view_helpers/action_view.rb:94
if Array === keys
defaults = keys.dup
key = defaults.shift
else
defaults = nil
key = keys
end
translate(key, **options.merge(:default => defaults, :scope => :will_paginate))
else
super
end
end
protected
def infer_collection_from_controller
collection_name = "@#{controller.controller_name}"
collection = instance_variable_get(collection_name)
raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
"forget to pass the collection object for will_paginate?" if collection.nil?
collection
end
class LinkRenderer < ViewHelpers::LinkRenderer
protected
GET_PARAMS_BLACKLIST = [:script_name, :original_script_name]
def default_url_params
{}
end
def url(page)
@base_url_params ||= begin
url_params = merge_get_params(default_url_params)
url_params[:only_path] = true
merge_optional_params(url_params)View on GitHub (pinned to 50017c3eb0)
Solutions
- Pass the collection explicitly: <%= will_paginate(@posts) %> — this removes the naming coupling entirely
- Or make the controller assign the conventional ivar: @posts = Post.paginate(page: params[:page] || 1) in PostsController
- In shared partials, always pass the collection as a local and render will_paginate posts or will_paginate(local_assigns[:posts])
Example fix
// before # controllers/posts_controller.rb def index @list = Post.paginate(page: params[:page] || 1) end # views/posts/index.html.erb <%= will_paginate %> // after # controllers/posts_controller.rb def index @posts = Post.paginate(page: params[:page] || 1) end # views/posts/index.html.erb <%= will_paginate(@posts) %>
Defensive patterns
Strategy: validation
Try / catch
begin
will_paginate
rescue ArgumentError => e
raise unless e.message.include?('appears to be empty')
nil # render nothing rather than 500 when the ivar was legitimately not set
end Prevention
- Standardize: index actions assign the ivar named after the resource (@posts in PostsController) as Rails convention expects
- Lint views for argument-less will_paginate calls in shared partials — require an explicit collection there
- Cover each list action with a request spec asserting the ivar is assigned; this fails before the view ever infers
When it happens
Trigger: PostsController#index that never runs @posts = Post.paginate(...), or assigns a differently named ivar (@list, @results) while the view relies on argument-less will_paginate; partials shared across controllers where controller_name doesn't match the ivar; copy-pasting a view into another controller without copying the ivar assignment.
Common situations: Renaming the ivar in the controller but not the view (or vice versa); refactoring index actions to local variables for render partial: locals; Typhpos like @post instead of @posts; a before_action that conditionally skips the assignment on some code path.
Related errors
- :renderer not specified
- :page parameter required
- unsupported parameters: %p
- invalid #{name}: #{value.inspect}
- DEPRECATION WARNING: #{message} (called from #{offending_lin
AI-assisted analysis of mislav/will_paginate@50017c3eb0 (2026-08-21).
Data as JSON: /api/errors/581487fe4c851d4e.
Report an issue: GitHub.