{"record":{"id":"cf6fbd1658a3a946","repo":"ruby-grape/grape","slug":"cannot-inherit-from-contract-only-schema","errorCode":null,"errorMessage":"Cannot inherit from contract, only schema","messagePattern":"Cannot inherit from contract, only schema","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/grape/dsl/validations.rb","lineNumber":21,"sourceCode":"module Grape\n  module DSL\n    module Validations\n      # Opens a root-level ParamsScope, defining parameter coercions and\n      # validations for the endpoint.\n      # @yield instance context of the new scope\n      def params(&)\n        Grape::Validations::ParamsScope.new(api: self, type: Hash, &)\n      end\n\n      # Declare the contract to be used for the endpoint's parameters.\n      # @param contract [Class<Dry::Validation::Contract> | Dry::Schema::Processor]\n      #   The contract or schema to be used for validation. Optional.\n      # @yield a block yielding a new instance of Dry::Schema::Params\n      #   subclass, allowing to define the schema inline. When the\n      #   +contract+ parameter is a schema, it will be used as a parent. Optional.\n      def contract(contract = nil, &block)\n        raise ArgumentError, 'Either contract or block must be provided' unless contract || block\n        raise ArgumentError, 'Cannot inherit from contract, only schema' if block && contract.respond_to?(:schema)\n\n        Grape::Validations::ContractScope.new(self, contract, &block)\n      end\n\n      private\n\n      # Clears all defined parameters and validations. The main purpose of it is to clean up\n      # settings, so next endpoint won't interfere with previous one.\n      #\n      #    params do\n      #      # params for the endpoint below this block\n      #    end\n      #    post '/current' do\n      #      # whatever\n      #    end\n      #\n      #    # somewhere between them the reset_validations! method gets called\n      #","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/ruby-grape/grape/blob/22d7975629846a3c0c7bd2b34e140a7a1b4af8f6/lib/grape/dsl/validations.rb#L3-L39","documentation":"`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.","triggerScenarios":"`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.","commonSituations":"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.","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."],"exampleFix":"# before\ncontract MyContract do\n  required(:extra).filled(:string)\nend # raises 'Cannot inherit from contract, only schema'\n\n# after\ncontract MyContract # no block\n\n# or inherit from the schema\ncontract MyContract.schema do\n  required(:extra).filled(:string)\nend","handlingStrategy":"type-guard","validationCode":"def schema_like?(obj) = !obj.respond_to?(:schema) # contracts respond to :schema and reject blocks\n\nraise ArgumentError, 'cannot add a block to a contract' if block && contract.respond_to?(:schema)","typeGuard":"def inheritable_contract?(obj) = obj.respond_to?(:schema) ? false : obj.is_a?(Class) || obj.is_a?(Dry::Schema::Processor)","tryCatchPattern":null,"preventionTips":["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."],"tags":["grape","contract","dry-validation","dry-schema","inheritance"],"backgroundTag":"contract-inheritance-mismatch","analyzedSha":"22d7975629846a3c0c7bd2b34e140a7a1b4af8f6","analyzedAt":"2026-08-21T17:03:54.627Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}