ruby-grape/grape · error · Grape::Exceptions::MissingGroupType

group type is required

Error message

group type is required

What it means

`optional :name do ... end` opens a nested parameter group, and Grape must know the container's shape to coerce and validate it. When a block is given but no `type:` option is supplied, Grape raises Grape::Exceptions::MissingGroupType ('group type is required') at boot time. The same rule applies to `requires` with a block; only flat attributes may omit the type.

Source

Thrown at lib/grape/dsl/parameters.rb:151

        return require_required_and_optional_fields(attrs.first, using:, except:) if using

        validate_attributes(attrs, **opts, &block)
        block ? new_scope(attrs.first, type: opts[:type], as: opts[:as], &block) : push_declared_params(attrs, as: opts[:as])
      end

      # Allow, but don't require, one or more parameters for the current
      #   endpoint.
      # @param (see #requires)
      # @option (see #requires)
      def optional(*attrs, using: nil, except: nil, **opts, &block)
        return redispatch_legacy_options(:optional, attrs, { using:, except: }.compact.merge(opts), &block) if legacy_options?(attrs)

        type = opts[:type]
        opts = @group.deep_merge(opts) if @group

        # check type for optional parameter group
        if attrs && block
          raise Grape::Exceptions::MissingGroupType if type.nil?
          raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
        end

        return require_optional_fields(attrs.first, using:, except:) if using

        validate_attributes(attrs, **opts, &block)
        block ? new_scope(attrs.first, type: opts[:type], as: opts[:as], optional: true, &block) : push_declared_params(attrs, as: opts[:as])
      end

      # Define common settings for one or more parameters
      # @param (see #requires)
      # @option (see #requires)
      def with(**opts, &)
        new_group_attrs = @group&.deep_merge(opts) || opts
        new_group_scope(new_group_attrs, &)
      end

      %i[mutually_exclusive exactly_one_of at_least_one_of all_or_none_of].each do |validator|

View on GitHub (pinned to 22d7975629)

Solutions

  1. Add `type: Hash` for a single nested object: `optional :address, type: Hash do ... end`.
  2. Use `type: Array` for a list of objects, or `type: JSON` / `type: Array[JSON]` for JSON payloads.

Example fix

# before
optional :address do
  requires :street, type: String
end # raises MissingGroupType

# after
optional :address, type: Hash do
  requires :street, type: String
end
Defensive patterns

Strategy: validation

Validate before calling

# Convention: every params block paired with `optional/requires ... do` MUST carry a group type
# Enforce in review/lint: grep for blocks without type:
#   grep -nE '(optional|requires)\s+:\w+(\s*,\s*)?do' config/api - shows suspicious declarations

Prevention

When it happens

Trigger: `optional :address do requires :street end` without a type. Copying a nested group example that relies on a default type (Grape has none). Refactoring `optional :address, type: Hash` by dropping the type while keeping the block.

Common situations: First-time use of nested params. Migrating from Grape versions or other frameworks (Rails strong params) where the container type is implicit. Renaming group parameters and accidentally removing the type option.

Related errors


AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21). Data as JSON: /api/errors/dfba8f4a2aa49936. Report an issue: GitHub.