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

  1. Pass the collection explicitly: <%= will_paginate(@posts) %> — this removes the naming coupling entirely
  2. Or make the controller assign the conventional ivar: @posts = Post.paginate(page: params[:page] || 1) in PostsController
  3. 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

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


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