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
This is CanCanCan's core authorization failure: Ability#authorize! (lib/cancan/ability.rb:180) raises CanCan::AccessDenied when cannot?(action, subject) is true. The generic 'You are not authorized to access this page.' text is used when no custom :message was passed and no unauthorized_message/i18n entry resolved. The raised exception carries action, subject, and conditions accessors, so you can inspect exactly which check failed.
Source
Thrown at lib/cancan/ability.rb:180
end
# User shouldn't specify targets with names of real actions or it will cause Seg fault
def validate_target(target)
error_message = "You can't specify target (#{target}) as alias because it is real action name"
raise Error, error_message if aliased_actions.values.flatten.include? target
end
def model_adapter(model_class, action)
adapter_class = ModelAdapters::AbstractAdapter.adapter_class(model_class)
adapter_class.new(model_class, relevant_rules_for_query(action, model_class))
end
# See ControllerAdditions#authorize! for documentation.
def authorize!(action, subject, *args)
message = args.last.is_a?(Hash) && args.last.key?(:message) ? args.pop[:message] : nil
if cannot?(action, subject, *args)
message ||= unauthorized_message(action, subject)
raise AccessDenied.new(message, action, subject, args)
end
subject
end
def attributes_for(action, subject)
attributes = {}
relevant_rules(action, subject).map do |rule|
attributes.merge!(rule.attributes_from_conditions) if rule.base_behavior
end
attributes
end
def has_block?(action, subject)
relevant_rules(action, subject).any?(&:only_block?)
end
def has_raw_sql?(action, subject)
relevant_rules(action, subject).any?(&:only_raw_sql?)View on GitHub (pinned to 8c1bf153a3)
Solutions
- Handle the exception globally: rescue_from CanCan::AccessDenied in ApplicationController and redirect or render 403.
- Add or fix the ability rule in app/models/ability.rb (e.g., can :manage, Article for the failing role), using the exception's action/subject to pinpoint it.
- Verify the ability wiring: current_user is set, the correct Ability class (Ability.new(current_user)) is used, and rule conditions match real column names.
- Pass a custom message (authorize! :edit, @article, message: '...') or add i18n entries under unauthorized_message for clearer UX.
Example fix
# before (app/models/ability.rb)
class Ability
include CanCan::Ability
def initialize(user)
can :read, Article
# no rule for :edit -> authorize! :edit raises AccessDenied
end
end
# after
class Ability
include CanCan::Ability
def initialize(user)
return unless user
can :manage, Article, owner_id: user.id
can :read, Article
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_path, alert: exception.message
end
end Defensive patterns
Strategy: try-catch
Validate before calling
# guard before authorizing (e.g., for soft-gated UI instead of an exception) return render_forbidden unless current_ability.can?(:edit, @article)
Type guard
def access_denied?(exception) exception.is_a?(CanCan::AccessDenied) end
Try / catch
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied do |exception|
Rails.logger.warn("ACCESS DENIED: #{exception.action} on #{exception.subject.inspect}")
respond_to do |format|
format.json { render json: { error: 'Forbidden' }, status: :forbidden }
format.html { redirect_to root_path, alert: exception.message }
end
end
end Prevention
- Use load_and_authorize_resource instead of hand-rolled authorize! calls so no action forgets the check.
- Cover every Ability with request specs asserting can?/cannot? for each role, using the be_able_to matcher.
- Log exception.action and exception.subject in the rescue block to pinpoint the missing rule fast.
- Instantiate the Ability with the real user object and fail closed (deny all) when user is nil.
When it happens
Trigger: Calling authorize! :edit, @article when the current ability has no matching 'can' rule; load_and_authorize_resource on a controller action whose rules deny access (e.g., a role Ability missing can :manage, Article); a rule whose condition hash or block evaluates false for that specific record.
Common situations: Forgot can :manage, :all for admin users; conditions hash key not matching record attributes (e.g., owner_id vs user_id); Devise session expired so current_user is nil and the default Ability denies everything; abilities defined per-role but the role column returned an unexpected value; nested or :through resources failing the parent check.
Related errors
- You are not authorized to access this page.
- This action failed the check_authorization because it does n
- You can't specify target (#{target}) as alias because it is
- The can? and cannot? call cannot be used with a raw sql 'can
- The accessible_by call cannot be used with a block 'can' def
AI-assisted analysis of CanCanCommunity/cancancan@8c1bf153a3 (2026-08-21).
Data as JSON: /api/errors/5569ef5d7323fb81.
Report an issue: GitHub.