{"record":{"id":"0164da9a7019ee62","repo":"ruby-concurrency/concurrent-ruby","slug":"offset-member-too-large-for-struct-size-lengt-0164da","errorCode":null,"errorMessage":"offset #{member} too large for struct(size:#{length})","messagePattern":"offset #(.+?) too large for struct\\(size:#(.+?)\\)","errorType":"exception","errorClass":"IndexError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/settable_struct.rb","lineNumber":79,"sourceCode":"    def each_pair(&block)\n      return enum_for(:each_pair) unless block_given?\n      synchronize { ns_each_pair(&block) }\n    end\n\n    # @!macro struct_select\n    def select(&block)\n      return enum_for(:select) unless block_given?\n      synchronize { ns_select(&block) }\n    end\n\n    # @!macro struct_set\n    #\n    # @raise [Concurrent::ImmutabilityError] if the given member has already been set\n    def []=(member, value)\n      if member.is_a? Integer\n        length = synchronize { @values.length }\n        if member >= length\n          raise IndexError.new(\"offset #{member} too large for struct(size:#{length})\")\n        end\n        synchronize do\n          unless @values[member].nil?\n            raise Concurrent::ImmutabilityError.new('struct member has already been set')\n          end\n          @values[member] = value\n        end\n      else\n        send(\"#{member}=\", value)\n      end\n    rescue NoMethodError\n      raise NameError.new(\"no member '#{member}' in struct\")\n    end\n\n    private\n\n    # @!visibility private\n    def initialize_copy(original)","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/settable_struct.rb#L61-L97","documentation":"SettableStruct instances are populated once per member; []= with an Integer index is bounds-checked against the member count and raises IndexError when index >= length. The struct is defined via SettableStruct.new(:a, :b) and instances of that generated class accept assignment by index or by name. Members can only be set while still nil; re-setting raises Concurrent::ImmutabilityError.","triggerScenarios":"s = K.new; s[2] = value on a struct with 2 members (valid indices 0..1); loop code using a computed index that runs one past the end; indexing with a stale constant after members were removed from the definition.","commonSituations":"Editing a struct definition (removing a member) while callers still assign by old positions; metaprogramming that maps array positions to struct slots; copy-paste between structs of different arity.","solutions":["Prefer named assignment: s.name = value instead of positional indexes","Bounds-check dynamic indexes: raise if index >= s.class.members.size","After changing a struct's member list, grep for numeric indexing into that struct and update the constants"],"exampleFix":"# before\nPerson = Concurrent::SettableStruct.new(:name, :age)\np = Person.new\np[2] = ' Berlin' # IndexError\n\n# after\np[1] = 30          # or better, by name:\np.age = 30","handlingStrategy":"validation","validationCode":"size = struct.class.members.size\nraise IndexError, \"index #{i} out of bounds\" unless i.is_a?(Integer) && i < size\nstruct[i] = value","typeGuard":"def valid_struct_index?(struct, i)\n  i.is_a?(Integer) && i >= 0 && i < struct.class.members.size\nend","tryCatchPattern":"begin\n  struct[i] = value\nrescue IndexError\n  logger.warn(\"dropping write to unknown slot #{i}\")\nend","preventionTips":["Prefer named member assignment over positional indexes","Keep member lists in a shared constant and derive loop bounds from members.size","Re-audit numeric indexing whenever a struct definition changes"],"tags":["ruby","struct","settable-struct","index-error","bounds-check"],"backgroundTag":"struct-index-out-of-bounds","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}