CanCanCommunity/cancancan · error · ArgumentError

accessible_by_strategy = :subquery requires ActiveRecord 5 o

Error message

accessible_by_strategy = :subquery requires ActiveRecord 5 or newer

What it means

CanCan.accessible_by_strategy= (lib/cancan/config.rb:71) raises ArgumentError when you set :subquery but the loaded ActiveRecord adapter is older than 5.0 (or the ActiveRecord adapter is not loaded at all, e.g., CanCanCan loaded outside Rails). The subquery strategy relies on 'FROM (subquery) alias' joins introduced in Rails 5, so it cannot work on Rails 4.x.

Source

Thrown at lib/cancan/config.rb:71

    @accessible_by_strategy = default_accessible_by_strategy
  end

  def self.default_accessible_by_strategy
    if does_not_support_subquery_strategy?
      # see https://github.com/CanCanCommunity/cancancan/pull/655 for where this was added
      # the `subquery` strategy (from https://github.com/CanCanCommunity/cancancan/pull/619
      # only works in Rails 5 and higher
      :left_join
    else
      :subquery
    end
  end

  def self.accessible_by_strategy=(value)
    validate_accessible_by_strategy!(value)

    if value == :subquery && does_not_support_subquery_strategy?
      raise ArgumentError, 'accessible_by_strategy = :subquery requires ActiveRecord 5 or newer'
    end

    @accessible_by_strategy = value
  end

  def self.with_accessible_by_strategy(value)
    return yield if value == accessible_by_strategy

    validate_accessible_by_strategy!(value)

    begin
      strategy_was = accessible_by_strategy
      @accessible_by_strategy = value
      yield
    ensure
      @accessible_by_strategy = strategy_was
    end
  end

View on GitHub (pinned to 8c1bf153a3)

Solutions

  1. Stay on the default :left_join strategy when stuck on Rails < 5 (it is the default precisely for this reason).
  2. Upgrade ActiveRecord/Rails to >= 5.0 to legitimately use :subquery.
  3. Guard the initializer: only assign when CanCan.valid_accessible_by_strategies.include?(:subquery).

Example fix

# before (config/initializers/cancan.rb)
CanCan.accessible_by_strategy = :subquery  # ArgumentError on Rails 4.2

# after
CanCan.accessible_by_strategy = :subquery if CanCan.valid_accessible_by_strategies.include?(:subquery)
Defensive patterns

Strategy: validation

Validate before calling

strategy = :subquery
raise ArgumentError, 'strategy unsupported on this ActiveRecord' unless CanCan.valid_accessible_by_strategies.include?(strategy)
CanCan.accessible_by_strategy = strategy

Try / catch

begin
  CanCan.accessible_by_strategy = :subquery
rescue ArgumentError => e
  Rails.logger.warn("#{e.message}; staying on :left_join")
end

Prevention

When it happens

Trigger: An initializer containing CanCan.accessible_by_strategy = :subquery in an app on Rails 4.2; a spec_helper that sets the strategy before the ActiveRecord adapter has been required; using CanCan::Config.with_accessible_by_strategy(:subquery) in a gem environment where ActiveRecord 4 is pinned.

Common situations: Copy-pasting the workaround initializer from a PR/blog post for the CanCanCan 619 duplicate-rows bug into a legacy Rails 4 app; CI running a different Rails version than production; loading cancancan in a Ruby-only (non-Rails) test suite.

Related errors


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