varvet/pundit · error · NoMethodError

You must define #resolve in #

Error message

You must define #resolve in #{self.class}

What it means

Pundit's generated ApplicationPolicy ships Scope#resolve as an abstract placeholder that raises NoMethodError. Every policy's nested Scope class must override #resolve to return the records the current user may see (typically an ActiveRecord::Relation or array). Pundit calls resolve after instantiating the scope class with (user, model), so inheriting the placeholder unimplemented makes the raise fire.

Solutions

  1. Open the policy class named in the error message and add a nested `class Scope` with a `def resolve` that returns the permitted collection, e.g. `scope.where(user: user)`.
  2. If everything should be visible to this policy's users, return `scope.all` (ActiveRecord) or just `scope` (PORO/array).
  3. If several scopes share the same rule, implement resolve once in a shared base class and inherit from that instead of ApplicationPolicy::Scope.
  4. Confirm the policy class in the message is the one being picked up (naming/namespace conventions) so you are editing the Scope Pundit actually instantiates.

Example fix

# before
class PostPolicy < ApplicationPolicy
  def show?
    true
  end
  # no Scope — controller calls policy_scope(Post) and hits ApplicationPolicy::Scope#resolve
end

# after
class PostPolicy < ApplicationPolicy
  def show?
    true
  end

  class Scope < ApplicationPolicy::Scope
    def resolve
      scope.where(user: user)
    end
  end
end
Defensive patterns

Strategy: validation

Validate before calling

scope_class = Pundit::PolicyFinder.new(record).scope

if scope_class.nil?
  # no scope class at all — policy_scope returns nil; policy_scope! would raise NotDefinedError
elsif scope_class.instance_method(:resolve).owner == ApplicationPolicy::Scope
  # would hit the raising placeholder — do not call policy_scope on this record yet
else
  pundit.policy_scope(record)
end

Type guard

def implements_resolve?(scope_class)
  return false if scope_class.nil?
  scope_class.instance_method(:resolve).owner != ApplicationPolicy::Scope
rescue NameError
  false
end

Try / catch

begin
  pundit.policy_scope(record)
rescue NoMethodError => e
  raise unless e.message.include?("#resolve") # only swallow the unimplemented-placeholder raise
  # scope not implemented for this policy — fall back or re-raise a domain error
  raise MissingPolicyScopeError, "#{record.class}Policy::Scope must define #resolve"
end

Prevention

When it happens

Trigger: Calling any scope-resolution API on a record whose policy Scope does not define #resolve: `pundit.policy_scope(Post)` / `Pundit.policy_scope(user, Post)` in a controller, `pundit.policy_scope!(Post)`, or `Pundit::PolicyFinder.new(Post).scope.resolve` — when `PostPolicy::Scope < ApplicationPolicy::Scope` and no `def resolve ... end` exists in it.

Common situations: Running the pundit:install generator, then hand-writing a policy with only query predicates (show?, edit?) and forgetting the nested Scope; copying a policy file that omits Scope; adding pundit to an existing app and only covering authorize-style checks before the first index action calls policy_scope; inheriting from ApplicationPolicy::Scope assuming the base provides a default resolve.

Understand the failure class

Background: "NotImplementedError: Subclasses should override this method" / "must be implemented" — abstract method errors explained — this error's family across 40 libraries.

Related errors


AI-assisted analysis of varvet/pundit@06318683c9 (2026-08-21). Data as JSON: /api/errors/7a39f9c3ac575a0b. Report an issue: GitHub.

Appendix: source

Thrown at lib/generators/pundit/install/templates/application_policy.rb.tt:46

    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  class Scope
    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      raise NoMethodError, "You must define #resolve in #{self.class}"
    end

    private

    attr_reader :user, :scope
  end
end

View on GitHub (pinned to 06318683c9)