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
endView on GitHub (pinned to 0b88d5ff75)
Solutions
- Bounds-check against size before assignment: use 0...struct.class.members.length ranges
- 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
- 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
- Iterate with 0...members.length (exclusive end), never 0..length
- Prefer named member access over index arithmetic where possible
- Validate externally sourced indices at the trust boundary (row/column numbers from input data)
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
- offset #{member} too large for struct(size:#{length})
- offset #{member} too large for struct(size:#{@values.length}
- wrong number of arguments (0 for 1+)
- no member '#{member}' in struct
- wrong number of arguments (0 for 1+)
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/028a7d7d7fd53036.
Report an issue: GitHub.