ruby-concurrency/concurrent-ruby · error · KeyError
key not found
Error message
key not found
What it means
Concurrent::Map#fetch mirrors Hash#fetch: when the key is absent and neither a default value nor a block is supplied, it raises KeyError ('key not found') via the private raise_fetch_no_key (map.rb:338). It is deliberately stricter than Map#[], which returns nil (or the default proc result) for missing keys.
Source
Thrown at lib/concurrent-ruby/concurrent/map.rb:328
end
# @!visibility private
def marshal_load(hash)
initialize
populate_from(hash)
end
undef :freeze
# @!visibility private
def inspect
format '%s entries=%d default_proc=%s>', to_s[0..-2], size.to_s, @default_proc.inspect
end
private
def raise_fetch_no_key
raise KeyError, 'key not found'
end
def initialize_copy(other)
super
populate_from(other)
end
def populate_from(hash)
hash.each_pair { |k, v| self[k] = v }
self
end
def validate_options_hash!(options)
if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0)
raise ArgumentError, ":initial_capacity must be a positive Integer"
end
if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1)
raise ArgumentError, ":load_factor must be a number between 0 and 1"View on GitHub (pinned to 0b88d5ff75)
Solutions
- Supply a default: map.fetch(key, default) or map.fetch(key) { compute }.
- If nil is acceptable for missing keys, use map[key] instead of fetch.
- Use map.fetch_or_store(key) { default } when you want atomic-ish read-or-populate.
- When the key must exist, fix the ordering so the writer runs before any reader.
Example fix
# before
port = services.fetch(:db) # KeyError: key not found if not yet registered
# after
port = services.fetch(:db) { default_db_port } # or services[:db] when nil is acceptable Defensive patterns
Strategy: validation
Validate before calling
value = map.key?(key) ? map.fetch(key) : default_value # note: not atomic under concurrent deletes; fetch(key, default) is safer
Try / catch
begin map.fetch(key) rescue KeyError default_value end
Prevention
- Default to fetch(key, default) where absence is normal.
- Reserve bare fetch for keys that must exist; treat KeyError as a bug signal.
- Use fetch_or_store for lazy initialization.
- Do not rely on key?-then-fetch under concurrency; a delete can interleave.
When it happens
Trigger: map.fetch(:missing) with no default and no block; a key deleted by another thread between a key? check and the fetch; reading a registry/config Map before the writer thread has populated it.
Common situations: Treating fetch like []; startup-order races where a background writer has not yet stored the key; check-then-fetch (TOCTOU) patterns on a shared Map; code ported from Hash that relied on nil returns.
Related errors
- Concurrent::MultipleAssignmentError
- can't dump hash with default proc
- :initial_capacity must be a positive Integer
- :load_factor must be a number between 0 and 1
- NullPointerException
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/5bf549395d4b6085.
Report an issue: GitHub.