ruby-concurrency/concurrent-ruby · error · ArgumentError
#{value} cannot be negative
Error message
#{value} cannot be negative What it means
ensure_positive rejects negative values with ArgumentError('x cannot be negative'); zero is allowed. It guards the counts of CountDownLatch (count) and Semaphore (permits, and the per-call amounts in acquire/release/drain-style methods). These primitives treat a negative count as a caller bug, since waiting threads could never be released.
Source
Thrown at lib/concurrent-ruby/concurrent/utility/native_integer.rb:39
value
end
def ensure_integer(value)
unless value.is_a?(Integer)
raise ArgumentError.new("#{value} is not an Integer")
end
value
end
def ensure_integer_and_bounds(value)
ensure_integer value
ensure_upper_bound value
ensure_lower_bound value
end
def ensure_positive(value)
if value < 0
raise ArgumentError.new("#{value} cannot be negative")
end
value
end
def ensure_positive_and_no_zero(value)
if value < 1
raise ArgumentError.new("#{value} cannot be negative or zero")
end
value
end
extend self
end
end
end
View on GitHub (pinned to 0b88d5ff75)
Solutions
- Clamp computed counts: Concurrent::CountDownLatch.new([count, 0].max) when zero is a valid degenerate case
- Fail loudly at the source when a negative count indicates a logic bug: raise ArgumentError if count < 0
- Guard per-call acquire/release amounts: raise if n < 0 before calling the primitive
Example fix
# before (empty list -> -1 -> ArgumentError) latch = Concurrent::CountDownLatch.new(items.size - 1) # after count = items.size - 1 latch = Concurrent::CountDownLatch.new(count.clamp(0..)) # or fail explicitly: raise ArgumentError, 'negative count' if count < 0
Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, "count #{count} must be >= 0" if count < 0
Concurrent::CountDownLatch.new(count) Type guard
def non_negative_int?(v) v.is_a?(Integer) && v >= 0 end
Try / catch
begin latch = Concurrent::CountDownLatch.new(count) rescue ArgumentError latch = Concurrent::CountDownLatch.new(0) # degenerate but valid; log the anomaly end
Prevention
- Clamp computed counts ([n, 0].max) when zero is a valid degenerate case
- Never derive latch counts from size - 1 without guarding the empty case
- Validate acquire/release amounts (>= 0) before calling semaphore methods
When it happens
Trigger: Concurrent::CountDownLatch.new(-1); Concurrent::Semaphore.new(-5); semaphore.acquire(-1) or semaphore.release(negative) with a computed amount; latch counts derived as items.size - completed.size going negative under a race or off-by-one.
Common situations: size - 1 computations on empty collections (e.g. CountDownLatch.new(list.size - 1) when list is empty); counts read from telemetry that can be negative; retry logic passing negative permit deltas.
Related errors
- #{value} cannot be negative or zero
- no block given
- Not all dependencies are IVars. Dependencies: #{ inputs.insp
- no block given
- number of threads must be greater than zero
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/086ee16b671fd8c4.
Report an issue: GitHub.