hasura/graphql-engine · error · Error
{} is defined on a non-composite type: {type_name}
Error message
{} is defined on a non-composite type: {type_name} What it means
Thrown when a fragment (named or inline) declares an `on` target that is not a composite type (object, interface, or union) — e.g. `fragment F on Int` or an inline fragment on a scalar. Raised from collect.rs (collect_fields_internal / around line 299-323) during field collection; the error names either 'inline fragment' or 'fragment <name>' plus the offending type name. GraphQL only allows fragment type conditions on composite types.
Source
Thrown at v3/crates/graphql/lang-graphql/src/validation/error.rs:16
use thiserror::Error;
use crate::ast::{common as ast, spanning};
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Error, Debug, Clone)]
pub enum Error {
#[error("fragment cycle detected through: {0:?}")]
CycleDetected(Vec<ast::Name>),
// TODO, this error isn't thrown yet
#[error("unused fragment: {0}")]
FragmentNotUsed(ast::Name),
#[error("fragment not defined in the document: {0}")]
UnknownFragment(ast::Name),
#[error("{} is defined on a non-composite type: {type_name}", match fragment_name { None => "inline fragment".to_owned(), Some(fragment_name) => format!("fragment {fragment_name}")})]
FragmentOnNonCompositeType {
fragment_name: Option<ast::Name>,
type_name: ast::TypeName,
},
#[error("fragment of type {fragment_type} cannot be spread on type {selection_type}")]
FragmentCannotBeSpread {
selection_type: ast::TypeName,
fragment_type: ast::TypeName,
},
// TODO, this error isn't thrown yet
#[error(
"a selection set is specified on field '{field_name}' of non-composite type: {type_name}"
)]
SelectionOnNonCompositeType {
field_name: ast::Name,
type_name: ast::TypeName,
},
#[error("no such field on type {type_name}: {field_name}")]View on GitHub (pinned to 724551b9ae)
Solutions
- Change the fragment's type condition to a composite type (object/interface/union) that exists in the schema
- If the parent field is a scalar/enum, remove the fragment/selection set entirely and select the scalar leaf directly
- Regenerate client queries against the current schema after schema changes
Example fix
// before
fragment F on Int { ... }
# after
fragment F on User { id } Defensive patterns
Strategy: validation
Validate before calling
let t = schema.lookup(&fragment.type_condition); assert!(t.map(|t| t.is_composite()).unwrap_or(false), "type condition must be composite");
Type guard
fn is_composite(t: &Type) -> bool { matches!(t, Type::Object(_) | Type::Interface(_) | Type::Union(_)) } Try / catch
match validate(doc) { Err(Error::FragmentOnNonCompositeType { fragment_name, type_name }) => fix_type_condition(fragment_name, type_name), r => r } Prevention
- Only write `on` conditions for object/interface/union types
- Use schema-aware IDE tooling that flags invalid type conditions
- Regenerate queries after schema type-kind changes
When it happens
Trigger: Writing `fragment F on String` (or any scalar/enum type) in a document; using an inline fragment `{ ... on MyEnum { ... } }` on a non-composite parent; applying fragments generated against a schema where the target used to be an object but is now a scalar/enum.
Common situations: Schema evolution changed a type from object to scalar or enum while stale queries still spread fragments on it; hand-written queries misunderstand that type conditions must be composite; codegen emitted fragments against the wrong schema version.
Related errors
- fragment not defined in the document: {0}
- validation failed: {0}
- Unexpected value: expecting {expected_kind:}, but found: {fo
- '{name:}' is not a valid GraphQL name.
- '{alias:} is not a valid alias
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/3f1721178c4c68d7.
Report an issue: GitHub.