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
endView on GitHub (pinned to 8c1bf153a3)
Solutions
- Add shallow: true when the resource is reachable both nested and non-nested: load_and_authorize_resource through: :project, shallow: true.
- Ensure the parent loads: correct route nesting, correct param name, and the through:/through_association options match the association.
- If the parent is mandatory, 404 early in the controller (raise ActiveRecord::RecordNotFound when the parent param is missing) before authorization runs.
- 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
- Add shallow: true whenever routes expose the child both nested and non-nested.
- Keep parent param names conventional (model_id) so ControllerAdditions finds them.
- Write controller specs hitting the non-nested route for every through: resource.
- If the parent is mandatory, 404 early in a before_action instead of letting authorization fail.
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
- You are not authorized to access this page.
- This action failed the check_authorization because it does n
- The accessible_by call cannot be used with a block 'can' def
- Association '#{key}' not defined in model '#{model_class.nam
- You can't specify target (#{target}) as alias because it is
AI-assisted analysis of CanCanCommunity/cancancan@8c1bf153a3 (2026-08-21).
Data as JSON: /api/errors/cb1f43c080eb34b1.
Report an issue: GitHub.