ruby-concurrency/concurrent-ruby · error · NameError
no member '#{member}' in struct
Error message
no member '#{member}' in struct What it means
When a non-Integer member is read, ns_get dispatches to the reader method via send(member); an undefined member raises NoMethodError, rescued and re-raised as NameError("no member 'x' in struct"). This matches core Struct semantics: only members declared at definition can be read, and there is no dynamic field support.
Source
Thrown at lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb:69
# @!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
#
# @!visibility private
def ns_each
values.each{|value| yield value }
end
# @!macro struct_each_pair
#View on GitHub (pinned to 0b88d5ff75)
Solutions
- Check membership before dynamic reads: s.class.members.include?(key.to_sym)
- Use to_h when you need Hash-like tolerant access (to_h returns only declared members)
- Keep member lists in one constant shared between the definition and dynamic accessors
Example fix
# before
Cfg = Concurrent::ImmutableStruct.new(:host, :port)
c = Cfg.new('localhost', 8080)
v = c[:prot] # NameError
# after
key = :port if Cfg.members.include?(:port)
v = key && c[key] Defensive patterns
Strategy: type-guard
Validate before calling
key = key.to_sym if key.is_a?(String) struct[key] if struct.class.members.include?(key)
Type guard
def known_member?(struct_class, key) struct_class.members.include?(key.to_sym) end
Try / catch
begin
v = struct[key]
rescue NameError => e
raise unless e.message.include?('no member')
v = struct.to_h[key.to_sym] # nil for extras; schema drift surfaced
end Prevention
- Whitelist external keys against Klass.members before reads
- Rename members with a deprecation alias period instead of a hard cut
- Prefer to_h when bridging structs to string-keyed payloads
When it happens
Trigger: s[:typo] or s.typo where the member was never declared or was renamed; symbol-vs-string key drift when forwarding params; de-serializing JSON into a struct assuming extra keys are readable.
Common situations: Renaming a member in the definition but not at read sites; schema drift between producer and consumer of the struct; using symbol literals copied from a different struct.
Related errors
- no member '#{member}' in struct
- identifier #{name} needs to be constant
- no member '#{member}' in struct
- offset #{member} too large for struct(size:#{@values.length}
- struct size differs
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/7a56ef728ec3733f.
Report an issue: GitHub.