CanCanCommunity/cancancan · error · CanCan::Error
Unable to merge an Active Record scope with other conditions
Error message
Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for #{rule_found.actions.first} #{rule_found.subjects.first} ability. What it means
The ActiveRecord adapter can honor at most ONE rule whose conditions are an ActiveRecord::Relation (a scope), and only when no other condition-carrying rules apply, because two relations or a relation plus a hash cannot be safely merged into one query. override_scope / raise_override_scope_error (lib/cancan/model_adapters/active_record_adapter.rb:184) raises CanCan::Error naming the first conflicting ability when accessible_by sees multiple relation conditions.
Source
Thrown at lib/cancan/model_adapters/active_record_adapter.rb:184
if base_hash[key].is_a?(Hash)
deep_merge(base_hash[key], value) unless value.empty?
else
base_hash[key] = value
end
end
end
def override_scope
conditions = @compressed_rules.map(&:conditions).compact
return unless conditions.any? { |c| c.is_a?(ActiveRecord::Relation) }
return conditions.first if conditions.size == 1
raise_override_scope_error
end
def raise_override_scope_error
rule_found = @compressed_rules.detect { |rule| rule.conditions.is_a?(ActiveRecord::Relation) }
raise Error,
'Unable to merge an Active Record scope with other conditions. ' \
"Instead use a hash or SQL for #{rule_found.actions.first} #{rule_found.subjects.first} ability."
end
def merge_conditions(sql, conditions_hash, behavior)
if conditions_hash.blank?
behavior ? true_sql : false_sql
else
merge_non_empty_conditions(behavior, conditions_hash, sql)
end
end
def merge_non_empty_conditions(behavior, conditions_hash, sql)
conditions = sanitize_sql(conditions_hash)
case sql
when true_sql
behavior ? true_sql : "not (#{conditions})"
when false_sqlView on GitHub (pinned to 8c1bf153a3)
Solutions
- Collapse the logic into one scope: can :read, Article, Article.where('active = ? OR featured = ?', true, true), or a custom class method scope combining the cases.
- Replace relation scopes with hash conditions wherever possible — hashes merge via OR in the adapter.
- Use a raw SQL string condition instead of a Relation (SQL strings merge like hashes).
- For index actions with irreconcilable scopes, fetch records manually per scope and combine in Ruby.
Example fix
# before
can :read, Article, Article.where(active: true)
can :read, Article, featured: true
# Article.accessible_by(ability) -> "Unable to merge an Active Record scope..."
# after
can :read, Article, Article.where('active = ? OR featured = ?', true, true)
# or pure hash:
can :read, Article, active: true
can :read, Article, featured: true Defensive patterns
Strategy: validation
Validate before calling
# before accessible_by: at most one Relation-conditioned rule, and nothing else with conditions
rules = current_ability.rules.select { |r| r.relevant?(:read, Article) && r.conditions.present? }
mergeable = rules.none? { |r| r.conditions.is_a?(ActiveRecord::Relation) } || (rules.size == 1 && rules.first.conditions.is_a?(ActiveRecord::Relation))
raise CanCan::Error, 'scope conditions unmergeable' unless mergeable Type guard
def relation_condition?(rule) rule.conditions.is_a?(ActiveRecord::Relation) end
Try / catch
begin
@articles = Article.accessible_by(current_ability)
rescue CanCan::Error => e
Rails.logger.warn("#{e.message}; falling back to union in Ruby")
@articles = Article.where(id: scope_a.ids | scope_b.ids)
end Prevention
- Prefer hash or SQL-string conditions in abilities; reserve scopes for single-rule setups.
- If multiple scoped roles exist for one model, combine them into one scope up front.
- Spec the index action per role combination so merges are exercised in CI.
- Watch multi-role users: rules merge across roles, and two roles each adding a scope is the classic trigger.
When it happens
Trigger: can :read, Article, Article.where(active: true) alongside can :read, Article, featured: true, then Article.accessible_by(current_ability); two scope rules for the same model (can :read, Post, Post.published and can :read, Post, Post.archived) both applying to the user; a single scope rule plus a hash rule on another role that merges into the same ability.
Common situations: Reusing existing model scopes as ability conditions (feels DRY) and later adding a second scoped role; multi-role users where each role contributes its own scope; index actions suddenly failing after a new permission is added.
Related errors
- The accessible_by call cannot be used with a block 'can' def
- accessible_by_strategy = :subquery requires ActiveRecord 5 o
- This model adapter does not support fetching records from th
- You are not authorized to access this page.
- You are not authorized to access this page.
AI-assisted analysis of CanCanCommunity/cancancan@8c1bf153a3 (2026-08-21).
Data as JSON: /api/errors/57c72eeb661fed22.
Report an issue: GitHub.