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

  1. Replace the raw SQL string with an equivalent conditions hash so instance checks work: can :read, Article, published: true.
  2. 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.
  3. Keep raw SQL only for abilities used exclusively in queries (accessible_by) and never call can? on instances of that subject.
  4. 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

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


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