{"record":{"id":"be6753698655b514","repo":"varvet/pundit","slug":"unable-to-find-scope-find-object-scope-for","errorCode":null,"errorMessage":"unable to find scope `#{find(object)}::Scope` for `#{object.inspect}`","messagePattern":"unable to find scope `#(.+?)::Scope` for `#(.+?)`","errorType":"exception","errorClass":"NotDefinedError","httpStatus":null,"severity":"error","filePath":"lib/pundit/policy_finder.rb","lineNumber":62,"sourceCode":"    # @return [nil, Class] policy class with query methods\n    # @see https://github.com/varvet/pundit#policies\n    # @example\n    #   policy = finder.policy #=> UserPolicy\n    #   policy.show? #=> true\n    #   policy.update? #=> false\n    #\n    # @since v0.1.0\n    def policy\n      klass = find(object)\n      klass.is_a?(String) ? klass.safe_constantize : klass\n    end\n\n    # @return [Scope{#resolve}] scope class which can resolve to a scope\n    # @raise [NotDefinedError] if scope could not be determined\n    #\n    # @since v0.1.0\n    def scope!\n      scope or raise NotDefinedError, \"unable to find scope `#{find(object)}::Scope` for `#{object.inspect}`\"\n    end\n\n    # @return [Class] policy class with query methods\n    # @raise [NotDefinedError] if policy could not be determined\n    #\n    # @since v0.1.0\n    def policy!\n      policy or raise NotDefinedError, \"unable to find policy `#{find(object)}` for `#{object.inspect}`\"\n    end\n\n    # @return [String] the name of the key this object would have in a params hash\n    #\n    # @since v1.1.0\n    def param_key # rubocop:disable Metrics/AbcSize\n      model = object.is_a?(Array) ? object.last : object\n\n      if model.respond_to?(:model_name)\n        model.model_name.param_key.to_s","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/varvet/pundit/blob/06318683c960066a2e499341cb372e0ff4540334/lib/pundit/policy_finder.rb#L44-L80","documentation":"PolicyFinder#scope! resolves the policy class by convention (`#{model}Policy`) and then tries to constantize `#{policy}::Scope`. When that constant does not exist it raises NotDefinedError showing the exact name it searched for. It is the strict variant — `scope` returns nil in the same situation, so this error means the bang API was used and the lookup failed.","triggerScenarios":"`pundit.policy_scope!(Post)`, `Pundit.policy_scope!(user, Post)`, or `Pundit::PolicyFinder.new(post).scope!` when: PostPolicy exists but has no nested `class Scope`; PostPolicy itself is missing (policy is nil, so `\"#{policy}::Scope\"` cannot resolve); a namespaced record `[:admin, Post]` lacks `Admin::PostPolicy::Scope`; a `policy_class` override on the model returns a class with no Scope nested inside.","commonSituations":"Generating a policy with query methods but omitting the nested Scope; namespaced models (Admin::Post) whose policy was scaffolded at top level; Rails/Zeitwerk naming mismatches where app/policies/admin/post_policy.rb defines the wrong constant; using symbols (`policy_scope!(:post)`) with no matching policy; switching from `policy_scope` (silent nil) to `policy_scope!` in a codebase with partially covered policies.","solutions":["Add a nested scope to the policy named in the message: `class Scope; def initialize(user, scope); ...; end; def resolve; ...; end; end`.","If the message shows an unexpected constant name, fix the policy class name/namespace to match convention (model class name + Policy, mirroring the model's namespace).","If you would rather get nil than a raise for uncovered models, call the non-bang `policy_scope` instead.","When overriding lookup, make the model's `policy_class` (or the array namespace form) point at a class that actually contains a nested Scope."],"exampleFix":"# before\nclass PostPolicy < ApplicationPolicy\n  def index?\n    false\n  end\n  # no Scope nested class\nend\n\npundit.policy_scope!(Post) # NotDefinedError: unable to find scope `PostPolicy::Scope`\n\n# after\nclass PostPolicy < ApplicationPolicy\n  class Scope < ApplicationPolicy::Scope\n    def resolve\n      scope.all\n    end\n  end\nend","handlingStrategy":"validation","validationCode":"scope_class = Pundit::PolicyFinder.new(record).scope # non-bang: returns nil instead of raising\n\nif scope_class\n  pundit.policy_scope!(record) # or policy_scope — class is known to exist now\nelse\n  # X::Scope missing: fall back, skip, or raise your own domain error\nend","typeGuard":"def policy_scope_defined?(record)\n  !Pundit::PolicyFinder.new(record).scope.nil?\nend","tryCatchPattern":"begin\n  pundit.policy_scope!(record)\nrescue Pundit::NotDefinedError => e\n  raise unless e.message.include?(\"::Scope\") # distinguish scope-missing from policy-missing\n  # decide deliberately: unscoped access is usually NOT safe to default\n  raise MissingPolicyScopeError, e.message\nend","preventionTips":["Treat every generated policy as incomplete until its nested Scope with #resolve exists; add a spec enumerating app/policies and asserting each policy defines Scope.","Keep policy file paths mirroring model namespaces (Admin::Post -> app/policies/admin/post_policy.rb) so `\"#{policy}::Scope\"` constantizes.","Use the bang variant only where absence is a bug; choose non-bang `policy_scope` plus an explicit nil check where absence is legitimate."],"tags":["ruby","pundit","authorization","policy-scope","naming-convention","class-not-found","not-defined-error"],"backgroundTag":"class-not-found","analyzedSha":"06318683c960066a2e499341cb372e0ff4540334","analyzedAt":"2026-08-21T18:13:24.520Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}