-
-
Notifications
You must be signed in to change notification settings - Fork 190
Open
Labels
Description
Describe the bug
When config.validate_keys = true is enabled and an unknown key error occurs at array index ≥ 10, dry-validation reports the error at approximately index / 10 (integer division) instead of the actual index.
To Reproduce
Executable script:
require 'dry-validation'
class TestContract < Dry::Validation::Contract
config.validate_keys = true
schema do
required(:items).array(:hash) do
required(:name).filled(:string)
required(:id).filled(:string)
end
end
end
# Create 88 items with unknown key at index 69
items = (0...88).map { |i| { name: "item-#{i}", id: "ID-#{i}" } }
items[69][:unknown_key] = 'value' # Add unknown key at index 69
contract = TestContract.new
result = contract.call({ items: items })
puts "Expected error at index: 69"
puts "Actual error at index: #{result.errors.first.path[1]}"
puts "Match: #{result.errors.first.path[1] == 69 ? 'PASS ✅' : 'FAIL ❌'}"
# Test the pattern across multiple indices
puts "\nPattern verification:"
[10, 20, 30, 40, 50, 60, 69, 70, 80, 90].each do |idx|
test_items = (0..idx).map { |i| { name: "item-#{i}", id: "ID-#{i}" } }
test_items[idx][:unknown] = 'val'
test_result = TestContract.new.call({ items: test_items })
reported = test_result.errors.first.path[1]
expected_if_bug = idx / 10
puts " Index #{idx.to_s.rjust(2)}: reported #{reported}, expected #{idx}, matches bug pattern (#{expected_if_bug}): #{reported == expected_if_bug ? 'YES ❌' : 'NO'}"
endSteps:
- Install dry-validation:
gem install dry-validation - Save the script above to a file (e.g.,
test_bug.rb) - Run:
ruby test_bug.rb
Expected behavior
The error should be reported at the correct index:
- Error path:
[:items, 69, :unknown_key] - Output:
"Actual error at index: 69"
Actual behavior
The error is reported at the wrong index:
- Error path:
[:items, 6, :unknown_key](should be 69, not 6) - Output:
"Actual error at index: 6"
Pattern observed:
| Actual Index | Reported Index | Pattern |
|---|---|---|
| 0-9 | 0-9 | ✅ Correct |
| 10-19 | 1 | index / 10 |
| 20-29 | 2 | index / 10 |
| 30-39 | 3 | index / 10 |
| 69 | 6 | index / 10 |
| 70-79 | 7 | index / 10 |
| 80-89 | 8 | index / 10 |
| 90-99 | 9 | index / 10 |
| 100+ | Wraps around | index % 100 |
My environment
- Affects my production application: YES (caused debugging confusion - errors were reported for wrong objects)
- dry-validation version: 1.11.1
- Ruby version: 3.4.2
- OS: macOS (Darwin 24.6.0)