ruby-concurrency/concurrent-ruby · error · IndexError
offset #{member} too large for struct(size:#{@values.length}
Error message
offset #{member} too large for struct(size:#{@values.length}) What it means
ns_get backs the reader side of all concurrent struct flavors (ImmutableStruct, MutableStruct, SettableStruct). Reading with an Integer index that is >= the number of members raises IndexError, mirroring core Struct's behavior. Valid indices are 0..length-1.
Source
Thrown at lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb:62
# @!visibility private
def ns_values_at(indexes)
@values.values_at(*indexes)
end
# @!macro struct_to_h
#
# @!visibility private
def ns_to_h
length.times.reduce({}){|memo, i| memo[self.class::MEMBERS[i]] = @values[i]; memo}
end
# @!macro struct_get
#
# @!visibility private
def ns_get(member)
if member.is_a? Integer
if member >= @values.length
raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
end
@values[member]
else
send(member)
end
rescue NoMethodError
raise NameError.new("no member '#{member}' in struct")
end
# @!macro struct_equality
#
# @!visibility private
def ns_equality(other)
self.class == other.class && self.values == other.values
end
# @!macro struct_each
#View on GitHub (pinned to 0b88d5ff75)
Solutions
- Bounds-check before indexed reads: index < s.class.members.size
- Iterate idiomatically via each/to_h/values instead of manual indexes
- Replace magic numeric indexes with named accessors (s.x) so removing a member becomes a NoMethodError at the right call site
Example fix
# before Point = Concurrent::MutableStruct.new(:x, :y) pt = Point.new(1, 2) coord = pt[2] # IndexError # after coord = pt[1] # last valid index coord = pt.to_h.values if idx >= 2 # or iterate
Defensive patterns
Strategy: validation
Validate before calling
if i.is_a?(Integer)
raise IndexError, "#{i} out of range" unless (0...struct.class.members.size).cover?(i)
end
value = struct[i] Type guard
def valid_struct_index?(struct, i) i.is_a?(Integer) && i >= 0 && i < struct.class.members.size end
Try / catch
begin v = struct[i] rescue IndexError v = nil # or fetch from struct.to_h with a default end
Prevention
- Use each/to_h/values for traversal instead of manual indexes
- Derive loop bounds from members.size, never hard-code them
- Replace numeric indexes with named readers so mistakes fail loudly at the right site
When it happens
Trigger: s[3] on a 2-member struct; each_with_index loops with an off-by-one bound (index <= members.size); array-like access patterns assumed from Hash#[] (which returns nil for missing keys).
Common situations: Off-by-one loop bounds; struct definitions trimmed while index-based readers remain; treating the struct as an Array in generic traversal code.
Related errors
- offset #{member} too large for struct(size:#{length})
- offset #{member} too large for struct(size:#{length})
- no member '#{member}' in struct
- struct size differs
- identifier #{name} needs to be constant
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/87b9e1139c0e0d40.
Report an issue: GitHub.