CanCanCommunity/cancancan · error · CanCan::AccessDenied

You are not authorized to access this page.

Error message

You are not authorized to access this page.

What it means

Raised by ControllerResourceLoader#resource_base_through (lib/cancan/controller_resource_loader.rb:82) while building the resource base for load_and_authorize_resource with the through: option. When no parent resource could be loaded and :shallow is not set, CanCanCan has no parent association to scope through and cannot even construct the query, so it raises CanCan::AccessDenied (with a nil/default message). The source comment notes this arguably should be a record-not-found error.

Source

Thrown at lib/cancan/controller_resource_loader.rb:82

      @options[:through] && [@options[:through]].flatten.detect { |i| fetch_parent(i) }
    end

    def resource_base_through_parent_resource
      if @options[:singleton]
        resource_class
      else
        parent_resource.send(@options[:through_association] || name.to_s.pluralize)
      end
    end

    def resource_base_through
      if parent_resource
        resource_base_through_parent_resource
      elsif @options[:shallow]
        resource_class
      else
        # maybe this should be a record not found error instead?
        raise AccessDenied.new(nil, authorization_action, resource_class)
      end
    end

    # The object that methods (such as "find", "new" or "build") are called on.
    # If the :through option is passed it will go through an association on that instance.
    # If the :shallow option is passed it will use the resource_class if there's no parent
    # If the :singleton option is passed it won't use the association because it needs to be handled later.
    def resource_base
      @options[:through] ? resource_base_through : resource_class
    end

    def parent_authorization_action
      @options[:parent_action] || :show
    end

    def authorization_action
      parent? ? parent_authorization_action : @params[:action].to_sym
    end

View on GitHub (pinned to 8c1bf153a3)

Solutions

  1. Add shallow: true when the resource is reachable both nested and non-nested: load_and_authorize_resource through: :project, shallow: true.
  2. Ensure the parent loads: correct route nesting, correct param name, and the through:/through_association options match the association.
  3. If the parent is mandatory, 404 early in the controller (raise ActiveRecord::RecordNotFound when the parent param is missing) before authorization runs.
  4. Keep the global rescue_from CanCan::AccessDenied as a safety net for UX.

Example fix

# before
class CommentsController < ApplicationController
  load_and_authorize_resource through: :project
  # GET /comments (non-nested) -> parent_resource nil -> AccessDenied
end

# after
class CommentsController < ApplicationController
  load_and_authorize_resource through: :project, shallow: true
end
Defensive patterns

Strategy: validation

Validate before calling

# run before load_and_authorize_resource builds the base (before_action)
raise ActiveRecord::RecordNotFound, 'parent project required' if params[:project_id].blank?

Try / catch

rescue_from CanCan::AccessDenied do |exception|
  redirect_to root_path, alert: 'Resource unavailable' # covers the nil-parent through: case too
end

Prevention

When it happens

Trigger: load_and_authorize_resource through: :project in a controller, but the request hits a route where params[:project_id] is absent so parent_resource is nil; the parent lookup returns nil (deleted record); through_association misconfigured so the parent never loads; using both nested and non-nested routes without shallow: true.

Common situations: Shallow-style routes mixed with nested through: resources and shallow: true forgotten; parent param named differently than the controller guesses; the parent record was destroyed but child routes still reachable; upgrading an app where route nesting changed.

Related errors


AI-assisted analysis of CanCanCommunity/cancancan@8c1bf153a3 (2026-08-21). Data as JSON: /api/errors/cb1f43c080eb34b1. Report an issue: GitHub.