CanCanCommunity/cancancan · error · CanCan::Error
The can? and cannot? call cannot be used with a raw sql 'can
Error message
The can? and cannot? call cannot be used with a raw sql 'can' definition. The checking code cannot be determined for #{action.inspect} #{subject.inspect} What it means
A rule defined with a raw SQL string, e.g. can :read, Project, "visibility = 'public'", can only be translated into SQL for queries; there is no way to evaluate a SQL fragment against an in-memory record. Rules#relevant_rules_for_match (lib/cancan/ability/rules.rb:63) therefore raises CanCan::Error whenever can?, cannot?, or authorize! encounters an only_raw_sql? rule for the requested action/subject.
Source
Thrown at lib/cancan/ability/rules.rb:63
end
def possible_relevant_rules(subject)
if subject.is_a?(Hash)
rules
else
positions = @rules_index.values_at(subject, *alternative_subjects(subject))
positions.compact!
positions.flatten!
positions.sort!
positions.map { |i| @rules[i] }
end
end
def relevant_rules_for_match(action, subject)
relevant_rules(action, subject).each do |rule|
next unless rule.only_raw_sql?
raise Error,
"The can? and cannot? call cannot be used with a raw sql 'can' definition. " \
"The checking code cannot be determined for #{action.inspect} #{subject.inspect}"
end
end
def relevant_rules_for_query(action, subject)
rules = relevant_rules(action, subject).reject do |rule|
# reject 'cannot' rules with attributes when doing queries
rule.base_behavior == false && rule.attributes.present?
end
if rules.any?(&:only_block?)
raise Error, "The accessible_by call cannot be used with a block 'can' definition." \
"The SQL cannot be determined for #{action.inspect} #{subject.inspect}"
end
rules
end
# Optimizes the order of the rules, so that rules with the :all subject are evaluated first.View on GitHub (pinned to 8c1bf153a3)
Solutions
- Replace the raw SQL string with an equivalent conditions hash so instance checks work: can :read, Article, published: true.
- Use a block for logic that must stay in Ruby: can :read, Article { |a| a.published? } — note this then breaks accessible_by, so prefer the hash when both are needed.
- Keep raw SQL only for abilities used exclusively in queries (accessible_by) and never call can? on instances of that subject.
- As a last resort, rescue CanCan::Error and fall back to a Ruby-side check for that subject.
Example fix
# before (app/models/ability.rb)
can :read, Article, "published_at <= NOW()"
# view: can?(:read, @article) -> CanCan::Error (raw sql cannot be checked)
# after
can :read, Article, :published => true # hash: works for can? AND accessible_by
# or, if logic must be Ruby-side:
can :read, Article, ->(_) { true }, where: 'published_at <= NOW()' # not supported; use block only when accessible_by is never called Defensive patterns
Strategy: validation
Validate before calling
# before any instance check on this subject
def instance_checkable?(ability, action, subject)
klass = subject.is_a?(Class) ? subject : subject.class
ability.rules.none? { |rule| rule.only_raw_sql? && rule.relevant?(action, klass) }
end
raise CanCan::Error, 'raw sql rule blocks can? checks' unless instance_checkable?(current_ability, :read, @article) Type guard
def raw_sql_rule?(rule) rule.respond_to?(:only_raw_sql?) && rule.only_raw_sql? end
Try / catch
begin
can?(:read, @article)
rescue CanCan::Error => e
Rails.logger.warn("cannot instance-check raw sql rule: #{e.message}")
false # fail closed
end Prevention
- Prefer conditions hashes over SQL strings; SQL strings are for query-only abilities only.
- If a SQL-string ability exists for a model, never call can?/authorize on its instances.
- Add a spec for every ability asserting can? works on a sample record — it catches raw-sql rules immediately.
- Rescue CanCan::Error and fail closed (deny) rather than letting the error 500.
When it happens
Trigger: Defining can :read, Article, 'published_at IS NOT NULL' in the Ability and then calling can?(:read, @article), cannot?(:read, @article), authorize! :read, @article, or the controller authorize helper on an instance; the same failure fires inside load_and_authorize_resource for single-record actions (show/edit/update/destroy).
Common situations: Using DB-specific SQL fragments (bit masks, function calls like NOW() < expires_at) for permissions and then using view guards <%= can?(:read, @article) %>; converting hash conditions to SQL strings for performance and forgetting that every instance check now explodes; index actions keep working (they compile to SQL) while show actions raise, which confuses debugging.
Related errors
- A hash of conditions is mutually exclusive with a block. Che
- You are not authorized to access this page.
- You can't specify target (#{target}) as alias because it is
- accessible_by_strategy = :subquery requires ActiveRecord 5 o
- accessible_by_strategy must be one of #{valid_accessible_by_
AI-assisted analysis of CanCanCommunity/cancancan@8c1bf153a3 (2026-08-21).
Data as JSON: /api/errors/bb9e8ae990ee82e1.
Report an issue: GitHub.