dry-rb/dry-validation

Arrays of multiple custom types not validating correctly

Open

#750 opened on Jul 11, 2026

View on GitHub
 (1 comment) (0 reactions) (0 assignees)Ruby (193 forks)github user discovery
bughelp wanted

Repository metrics

Stars
 (1,421 stars)
PR merge metrics
 (PR metrics pending)

Description

This bug report is being submitted following on from this forum discussion. There's more context there if necessary, but I'll try to keep to the specifics of the actual issue in this report.

Note: I wasn't sure whether to open the report on this repo or on dry-schema repo (I guess it's mainly a schema issue but since it occurred in the context of using dry-validation I thought I would open it here so that the examples/steps to reproduce make sense).

Describe the bug

When defining a schema in a Dry::Validation::Contract class definition that contains an array of multiple custom types, then invoking call on that contract while passing in a data structure which should be invalid against the defined schema, the returned object returns true when valid? is called on it, and the errors hash is empty.

To Reproduce

Given the following Dry::Validation::Contract class definition:

require 'dry-validation'
require 'dry-schema'
require 'dry-types'

Types = Dry.Types()

Color = Types::Hash.schema(type: Types::String, name: Types::String, code: Types::Integer)
Book = Types::Hash.schema(type: Types::String, title: Types::String, genre: Types::String)

class ThingsContract < Dry::Validation::Contract
  schema do
    required(:things).value(array[Color | Book])
  end
end

Instantiate a new contract object:

things_contract = ThingsContract.new

Define some data which includes a Hash which doesn't match the structure of either the Color or Book types, and should therefore be invalid.

invalid_things = {
   things: [ 
     { type: 'Color', name: 'red', code: 1 },
     { type: 'Book', title: 'The Great Gatsby', genre: 'Fiction' },
     { type: 'Foo', foo: 'bar' } # This should fail validation since it does not match either `Color` or `Book` types
  ]
}

Pass that data into the call invocation:

invalid_things_result = person_contract.call(invalid_person)
# invalid_things_result.success? => true
# invalid_things_result.errors.to_h => {}

Expected behavior

The expected behaviour would be that invalid_things_result.success? returns false, and that invalid_things_result.errors.to_h would not return an empty hash.

My environment

  • Ruby version: 3.4
  • OS: Mac OS (ventura 13.5)

Contributor guide