sidekiq/sidekiq · error · ArgumentError
array must be an Array
Error message
array must be an Array
What it means
`array_enumerator(array, cursor:)` is the iterable helper that turns a plain Ruby Array into a resumable Enumerator. It only accepts an actual Array; anything else raises ArgumentError immediately, because the cursor logic relies on `each_with_index.drop(cursor)` array semantics.
Source
Thrown at lib/sidekiq/job/iterable/enumerators.rb:21
require_relative "active_record_enumerator"
require_relative "csv_enumerator"
module Sidekiq
module Job
module Iterable
module Enumerators
# Builds Enumerator object from a given array, using +cursor+ as an offset.
#
# @param array [Array]
# @param cursor [Integer] offset to start iteration from
#
# @return [Enumerator]
#
# @example
# array_enumerator(['build', 'enumerator', 'from', 'any', 'array'], cursor: cursor)
#
def array_enumerator(array, cursor:)
raise ArgumentError, "array must be an Array" unless array.is_a?(Array)
x = array.each_with_index.drop(cursor || 0)
x.to_enum { x.size }
end
# Builds Enumerator from `ActiveRecord::Relation`.
# Each Enumerator tick moves the cursor one row forward.
#
# @param relation [ActiveRecord::Relation] relation to iterate
# @param cursor [Object] offset id to start iteration from
# @param options [Hash] additional options that will be passed to relevant
# ActiveRecord batching methods
#
# @return [ActiveRecordEnumerator]
#
# @example
# def build_enumerator(cursor:)
# active_record_records_enumerator(User.all, cursor: cursor)View on GitHub (pinned to 1bb4aa06e5)
Solutions
- Materialize to an Array before calling the helper: `array_enumerator(items.to_a, cursor: cursor)`.
- For ActiveRecord relations use `active_record_records_enumerator(relation, cursor:)` instead — it batches efficiently and doesn't need an Array.
- For other enumerables (Range, Set), call `.to_a` first or build a custom `.to_enum { size }` enumerator.
Example fix
// before def build_enumerator(cursor:) array_enumerator((1..100), cursor: cursor) # Range -> ArgumentError end // after def build_enumerator(cursor:) array_enumerator((1..100).to_a, cursor: cursor) end
Defensive patterns
Strategy: type-guard
Validate before calling
items = items.to_a unless items.is_a?(Array) raise ArgumentError, 'array_enumerator needs an Array' unless items.is_a?(Array) array_enumerator(items, cursor: cursor)
Type guard
def array_input?(obj) obj.is_a?(Array) end
Try / catch
begin array_enumerator(items, cursor: cursor) rescue ArgumentError array_enumerator(items.to_a, cursor: cursor) # only if items is safely materializable end
Prevention
- Call .to_a on ranges/sets/lazy enumerables before the helper, or pick the matching helper (active_record_records_enumerator for relations).
- Unit-test build_enumerator against the real shape of your data source.
- Beware nil from params/JSON — guard with Array(items).to_a when appropriate.
When it happens
Trigger: Passing a Range (`1..100`), a Set, an ActiveRecord::Relation that was `.to_a`-ed conditionally, an Enumerator, or nil to `array_enumerator`. The raise happens when `build_enumerator` runs on the worker (or in tests that instantiate the enumerator).
Common situations: Calling `.map`/`.select` chains that lazily return enumerators instead of arrays. Passing `Model.pluck(:id)` is fine (Array), but `Model.all` (Relation) or `1..50.times` style values are not. Data loaded from JSON or params that is actually nil or a Hash.
Related errors
- #build_enumerator must return an Enumerator, but returned #{
- CsvEnumerator.new takes CSV object
- #{self.class.name} must implement a '#build_enumerator' meth
- #{self.class.name} must implement an '#each_iteration' metho
- Job arguments to #{job_class} must be native JSON types, but
AI-assisted analysis of sidekiq/sidekiq@1bb4aa06e5 (2026-08-21).
Data as JSON: /api/errors/09be3ea6f43dbe01.
Report an issue: GitHub.