ruby-concurrency/concurrent-ruby · error · IndexError

offset #{member} too large for struct(size:#{length})

Error message

offset #{member} too large for struct(size:#{length})

What it means

MutableStruct instances store member values in a fixed-length array; #[]= with an Integer index checks it against the number of members and raises IndexError 'offset N too large for struct(size:M)' when the index is at or beyond the size. This mirrors core Ruby's Struct#[]= bounds checking.

Source

Thrown at lib/concurrent-ruby/concurrent/mutable_struct.rb:189

    # @!macro struct_set
    #
    #   Attribute Assignment
    #
    #   Sets the value of the given struct member or the member at the given index.
    #
    #   @param [Symbol, String, Integer] member the string or symbol name of the member
    #     for which to obtain the value or the member's index
    #
    #   @return [Object] the value of the given struct member or the member at the given index.
    #
    #   @raise [NameError] if the name does not exist
    #   @raise [IndexError] if the index is out of range.
    def []=(member, value)
      if member.is_a? Integer
        length = synchronize { @values.length }
        if member >= length
          raise IndexError.new("offset #{member} too large for struct(size:#{length})")
        end
        synchronize { @values[member] = value }
      else
        send("#{member}=", value)
      end
    rescue NoMethodError
      raise NameError.new("no member '#{member}' in struct")
    end

    private

    # @!visibility private
    def initialize_copy(original)
      synchronize do
        super(original)
        ns_initialize_copy
      end
    end

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Bounds-check against size before assignment: use 0...struct.class.members.length ranges
  2. Prefer named access: point.x = 1 or point[:x] = 1 — typos surface as NameError at the same place but are easier to spot in review
  3. When the index comes from external data, validate and clamp or reject it at the boundary

Example fix

# before
struct[i] = value # i can reach struct.class.members.length

# after
if i >= 0 && i < struct.class.members.length
  struct[i] = value
else
  raise IndexError, "index #{i} outside 0...#{struct.class.members.length}"
end
Defensive patterns

Strategy: type-guard

Validate before calling

size = struct.class.members.length
raise IndexError, "index #{i} outside 0...#{size}" unless i.is_a?(Integer) && i >= -size && i < size
struct[i] = value

Type guard

->(struct, i) { i.is_a?(Integer) && i >= -struct.class.members.length && i < struct.class.members.length }

Try / catch

begin
  struct[i] = value
rescue IndexError => e
  raise RangeError, "bad column index #{i}: #{e.message}"
end

Prevention

When it happens

Trigger: point[2] = 3 on a two-member struct; loops that iterate 0..count instead of 0...count-1; computing an index from external data (row/column numbers) without clamping; off-by-one after refactoring member lists.

Common situations: Translating CSV/DB column positions to struct indices where a wider row hits a narrower struct; loops written against an older member list; index arithmetic with interpolated user input.

Related errors


AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21). Data as JSON: /api/errors/028a7d7d7fd53036. Report an issue: GitHub.