ruby-grape/grape · error · ArgumentError
Representation of type #{representation.class} cannot be mer
Error message
Representation of type #{representation.class} cannot be merged. What it means
When `present` is called while a response body already exists and an entity class is involved, Grape merges the new representation into the existing body with `merge`. Entities representing a collection produce an Array (or another non-Hash) value, which does not respond to `merge`, so Grape raises 'Representation of type Array cannot be merged.' instead of silently producing a broken body.
Source
Thrown at lib/grape/dsl/entity.rb:37
# @param options [Hash] additional options forwarded to the entity's `represent` call.
#
# @example
#
# get '/users/:id' do
# present User.find(params[:id]),
# with: API::Entities::User,
# admin: current_user.admin?
# end
def present(*args, root: nil, with: nil, **options)
key, object = args.count == 2 && args.first.is_a?(Symbol) ? args : [nil, args.first]
entity_class = with || entity_class_for_obj(object)
representation = entity_class ? entity_representation_for(entity_class, object, options) : object
representation = { root => representation } if root
if key
representation = body&.merge(key => representation) || { key => representation }
elsif entity_class.present? && body
raise ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
representation = body.merge(representation)
end
body representation
end
# Attempt to locate the Entity class for a given object, if not given
# explicitly. This is done by looking for the presence of Klass::Entity,
# where Klass is the class of the `object` parameter, or one of its
# ancestors. Object is excluded from the search: top-level constants
# live on it, so a global ::Entity class is not a representer.
# @param object [Object] the object to locate the Entity class for
# @return [Class] the located Entity class, or nil if none is found
def entity_class_for_obj(object)
entity_for_class(object.class) || entity_for_class(element_class(object))
end
View on GitHub (pinned to 22d7975629)
Solutions
- Present collections under a key so the wrapper Hash is mergeable: `present :cars, cars, with: CarEntity`.
- Use the `root:` option to wrap the representation in a Hash: `present cars, with: CarEntity, root: 'cars'`.
- Present a single composite entity for the whole response, or build the response hash manually and present it once.
Example fix
# before get '/dashboard' do present user, with: UserEntity present cars, with: CarEntity # Array cannot be merged end # after get '/dashboard' do present :user, user, with: UserEntity present :cars, cars, with: CarEntity end
Defensive patterns
Strategy: validation
Validate before calling
# Keyed presents always merge (the wrapper is a Hash), so prefer them for compound bodies present :user, user, with: UserEntity present :cars, cars, with: CarEntity
Type guard
# before a second bare present of a collection, check mergeability of the entity output representation = CarEntity.represent(cars) raise ArgumentError, 'use a key/root for collection entities' unless representation.respond_to?(:merge)
Prevention
- Adopt the keyed form `present :key, object, with: Entity` for every additional section of a response.
- One endpoint, one root entity: model compound responses as a single parent entity.
- Spec compound endpoints so a second collection present fails at test time, not at runtime.
When it happens
Trigger: Two `present` calls in one endpoint where the later one presents a collection with an entity: `present user, with: UserEntity` followed by `present cars, with: CarEntity` (the second representation is an Array). Presenting any entity whose root-level output is not a Hash when a body is already set.
Common situations: Building a compound response (user + their items) by calling present once per section. Returning an array entity after a `present status_message` style call. Migration from single-object endpoints to endpoints that also include collections.
Related errors
- type #{type} should support coercion via `[]`
- #declared is not available prior to parameter validation
- Status code must be Integer or Symbol.
- Argument must be a file path
- Stream object must respond to :each.
AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21).
Data as JSON: /api/errors/e3d1031fb30dbb92.
Report an issue: GitHub.