ruby-grape/grape · error · ArgumentError
Cannot inherit from contract, only schema
Error message
Cannot inherit from contract, only schema
What it means
`contract MyContract do ... end` would need to create a schema that inherits from the passed object, and only Dry::Schema processors can act as schema parents. Dry::Validation::Contract classes expose their schema through a `schema` method (hence the `respond_to?(:schema)` probe), and a contract's rules cannot be merged into an inline schema block, so this combination raises ArgumentError.
Source
Thrown at lib/grape/dsl/validations.rb:21
module Grape
module DSL
module Validations
# Opens a root-level ParamsScope, defining parameter coercions and
# validations for the endpoint.
# @yield instance context of the new scope
def params(&)
Grape::Validations::ParamsScope.new(api: self, type: Hash, &)
end
# Declare the contract to be used for the endpoint's parameters.
# @param contract [Class<Dry::Validation::Contract> | Dry::Schema::Processor]
# The contract or schema to be used for validation. Optional.
# @yield a block yielding a new instance of Dry::Schema::Params
# subclass, allowing to define the schema inline. When the
# +contract+ parameter is a schema, it will be used as a parent. Optional.
def contract(contract = nil, &block)
raise ArgumentError, 'Either contract or block must be provided' unless contract || block
raise ArgumentError, 'Cannot inherit from contract, only schema' if block && contract.respond_to?(:schema)
Grape::Validations::ContractScope.new(self, contract, &block)
end
private
# Clears all defined parameters and validations. The main purpose of it is to clean up
# settings, so next endpoint won't interfere with previous one.
#
# params do
# # params for the endpoint below this block
# end
# post '/current' do
# # whatever
# end
#
# # somewhere between them the reset_validations! method gets called
#View on GitHub (pinned to 22d7975629)
Solutions
- Use the contract as-is with no block: `contract MyContract`.
- If you need customization, pass the underlying schema (`MyContract.schema`) with the block - schemas support inheritance.
- Better: define a new contract class that composes/reuses the rules of the original.
Example fix
# before contract MyContract do required(:extra).filled(:string) end # raises 'Cannot inherit from contract, only schema' # after contract MyContract # no block # or inherit from the schema contract MyContract.schema do required(:extra).filled(:string) end
Defensive patterns
Strategy: type-guard
Validate before calling
def schema_like?(obj) = !obj.respond_to?(:schema) # contracts respond to :schema and reject blocks raise ArgumentError, 'cannot add a block to a contract' if block && contract.respond_to?(:schema)
Type guard
def inheritable_contract?(obj) = obj.respond_to?(:schema) ? false : obj.is_a?(Class) || obj.is_a?(Dry::Schema::Processor)
Prevention
- Keep contracts blockless; customize by subclassing the Dry::Validation::Contract instead.
- Pass `Contract.schema` when you truly want schema inheritance with an inline block.
- Standardize on one style per codebase (contracts vs schemas) to avoid mixing.
When it happens
Trigger: `contract MyContract do required(:extra).filled end` where MyContract < Dry::Validation::Contract. Passing a contract class read from configuration and also adding a tweak block. Copying a schema-with-block example but substituting a contract class.
Common situations: Trying to extend a shared contract with endpoint-specific keys. Migration from schemas to contracts while keeping the inline-block style. Mixing up Dry::Schema::Params and Dry::Validation::Contract in a codebase that uses both.
Related errors
- Either contract or block must be provided
- type #{type} should support coercion via `[]`
- #declared is not available prior to parameter validation
- Representation of type #{representation.class} cannot be mer
- Status code must be Integer or Symbol.
AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21).
Data as JSON: /api/errors/cf6fbd1658a3a946.
Report an issue: GitHub.