{"record":{"id":"57c72eeb661fed22","repo":"CanCanCommunity/cancancan","slug":"unable-to-merge-an-active-record-scope-with-other","errorCode":null,"errorMessage":"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.","messagePattern":"Unable to merge an Active Record scope with other conditions\\. Instead use a hash or SQL for #(.+?) #(.+?) ability\\.","errorType":"exception","errorClass":"CanCan::Error","httpStatus":null,"severity":"error","filePath":"lib/cancan/model_adapters/active_record_adapter.rb","lineNumber":184,"sourceCode":"          if base_hash[key].is_a?(Hash)\n            deep_merge(base_hash[key], value) unless value.empty?\n          else\n            base_hash[key] = value\n          end\n        end\n      end\n\n      def override_scope\n        conditions = @compressed_rules.map(&:conditions).compact\n        return unless conditions.any? { |c| c.is_a?(ActiveRecord::Relation) }\n        return conditions.first if conditions.size == 1\n\n        raise_override_scope_error\n      end\n\n      def raise_override_scope_error\n        rule_found = @compressed_rules.detect { |rule| rule.conditions.is_a?(ActiveRecord::Relation) }\n        raise Error,\n              'Unable to merge an Active Record scope with other conditions. ' \\\n              \"Instead use a hash or SQL for #{rule_found.actions.first} #{rule_found.subjects.first} ability.\"\n      end\n\n      def merge_conditions(sql, conditions_hash, behavior)\n        if conditions_hash.blank?\n          behavior ? true_sql : false_sql\n        else\n          merge_non_empty_conditions(behavior, conditions_hash, sql)\n        end\n      end\n\n      def merge_non_empty_conditions(behavior, conditions_hash, sql)\n        conditions = sanitize_sql(conditions_hash)\n        case sql\n        when true_sql\n          behavior ? true_sql : \"not (#{conditions})\"\n        when false_sql","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/CanCanCommunity/cancancan/blob/8c1bf153a3da7b2261d6fa4a5f84eb28e2feb828/lib/cancan/model_adapters/active_record_adapter.rb#L166-L202","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\ncan :read, Article, Article.where(active: true)\ncan :read, Article, featured: true\n# Article.accessible_by(ability) -> \"Unable to merge an Active Record scope...\"\n\n# after\ncan :read, Article, Article.where('active = ? OR featured = ?', true, true)\n# or pure hash:\ncan :read, Article, active: true\ncan :read, Article, featured: true","handlingStrategy":"validation","validationCode":"# before accessible_by: at most one Relation-conditioned rule, and nothing else with conditions\nrules = current_ability.rules.select { |r| r.relevant?(:read, Article) && r.conditions.present? }\nmergeable = rules.none? { |r| r.conditions.is_a?(ActiveRecord::Relation) } || (rules.size == 1 && rules.first.conditions.is_a?(ActiveRecord::Relation))\nraise CanCan::Error, 'scope conditions unmergeable' unless mergeable","typeGuard":"def relation_condition?(rule)\n  rule.conditions.is_a?(ActiveRecord::Relation)\nend","tryCatchPattern":"begin\n  @articles = Article.accessible_by(current_ability)\nrescue CanCan::Error => e\n  Rails.logger.warn(\"#{e.message}; falling back to union in Ruby\")\n  @articles = Article.where(id: scope_a.ids | scope_b.ids)\nend","preventionTips":["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."],"tags":["accessible-by","scopes","conditions-merge","active-record","cancancan"],"backgroundTag":"unmergeable-scope-conditions","analyzedSha":"8c1bf153a3da7b2261d6fa4a5f84eb28e2feb828","analyzedAt":"2026-08-21T20:05:55.000Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}