Composition Rules and Errors
Rules for composing subgraph schemas into a supergraph, with error codes and fixes.
Federation Versions: Floor vs. Composition
A subgraph's @link(url: ".../federation/vX.Y") version is the minimum
version required for the directives that subgraph uses — not a declaration
of what version the graph is composed at. Two versions are in play, and they are
set independently:
- Subgraph floor — the
@linkversion in each subgraph's SDL. It just needs to cover the directives that subgraph actually uses. - Composition version — set separately, once, for the whole build:
federation_versioninsupergraph.yamlfor local composition, or the variant's Build Pipeline setting in GraphOS.
The only rule between them is: the composition version must be ≥ every
subgraph's floor. A subgraph sitting below the composition version is normal,
not a bug — you do not need to bump every subgraph's @link to match the
composition version.
The version numbers used throughout this skill (e.g.
v2.12,=2.9.0) are illustrative. Check the Federation changelog for currently supported versions before pinning one, rather than copying whatever number appears in an example.
UNKNOWN_FEDERATION_LINK_VERSION at server startup
If buildSubgraphSchema() throws UNKNOWN_FEDERATION_LINK_VERSION at server
startup (not at rover subgraph publish / composition time), that's a
client-library lag — not a sign that GraphOS doesn't support the version.
Composition (Rust, in Rover/Router) and the JS schema-building library you build
your subgraph with (@apollo/subgraph) have independent, separately-versioned
understandings of which federation versions exist, and the JS side can trail
behind. So a version can compose fine in GraphOS yet be rejected by your local
subgraph server.
Fix: lower that subgraph's @link to the highest version your library
actually recognizes (upgrade @apollo/subgraph if you need a newer one). The
composition version can still be pinned higher — remember the floor-vs-composition
distinction above.
Entity Validation
Entities must have valid @key definitions that can be resolved across subgraphs.
KEY_FIELDS_SELECT_INVALID_TYPE
@key includes a field returning list, interface, or union.
# INVALID
type Product @key(fields: "tags") {
tags: [String!]! # list not allowed in key
}
# VALID
type Product @key(fields: "id") {
id: ID!
tags: [String!]!
}Use only scalar, enum, or object fields in keys.
KEY_FIELDS_HAS_ARGS
@key includes a field with arguments.
# INVALID
type Product @key(fields: "name") {
name(locale: String!): String! # args not allowed in key
}
# VALID - use a field without arguments
type Product @key(fields: "id") {
id: ID!
name(locale: String!): String!
}KEY_INVALID_FIELDS
Invalid syntax or unknown fields in @key.
# INVALID
type Product @key(fields: "sku") {
id: ID! # "sku" doesn't exist
}
# VALID
type Product @key(fields: "id") {
id: ID!
}Check field names and syntax: @key(fields: "id") or @key(fields: "id organization { id }").
INTERFACE_KEY_NOT_ON_IMPLEMENTATION
Entity interface has @key but an implementation doesn't.
# INVALID
interface Media @key(fields: "id") {
id: ID!
}
type Book implements Media { # missing @key
id: ID!
}
# VALID
type Book implements Media @key(fields: "id") {
id: ID!
}All implementations must have the same @key(s) as the interface.
Shareability
Fields resolved by multiple subgraphs must be explicitly marked @shareable.
INVALID_FIELD_SHARING
Field resolved by multiple subgraphs without @shareable.
# INVALID
type Position {
x: Int!
}
# VALID
type Position @shareable {
x: Int!
}Add @shareable to the field or type in all subgraphs.
SHAREABLE_HAS_MISMATCHED_RUNTIME_TYPES
Shareable field has incompatible types across subgraphs.
# INVALID
# Subgraph A
type Event @shareable {
timestamp: Int!
}
# Subgraph B
type Event @shareable {
timestamp: String! # incompatible with Int!
}Nullable can coerce to non-nullable, but base types must be compatible.
External Fields
Fields marked @external must exist in another subgraph and be used by a directive.
EXTERNAL_MISSING_ON_BASE
@external field not defined in any other subgraph.
Define the field in the originating subgraph, or remove @external.
EXTERNAL_UNUSED
@external field not used by @key, @requires, or @provides.
Either use the field in a directive or remove it.
EXTERNAL_TYPE_MISMATCH
@external field type doesn't match the original definition.
Align the type with the originating subgraph.
Provides/Requires
Fields referenced in @provides and @requires must be properly declared as @external.
PROVIDES_FIELDS_MISSING_EXTERNAL
@provides field not marked @external.
# INVALID
type Product @key(fields: "id") {
id: ID!
name: String! # missing @external
}
type Query {
products: [Product!]! @provides(fields: "name")
}
# VALID
type Product @key(fields: "id") {
id: ID!
name: String! @external
}
type Query {
products: [Product!]! @provides(fields: "name")
}REQUIRES_FIELDS_MISSING_EXTERNAL
@requires field not marked @external.
# INVALID
type Product @key(fields: "id") {
id: ID!
weight: Int # missing @external
shippingCost: Int @requires(fields: "weight")
}
# VALID
type Product @key(fields: "id") {
id: ID!
weight: Int @external
shippingCost: Int @requires(fields: "weight")
}Override
The @override directive has strict rules about which fields it can be applied to.
OVERRIDE_FROM_SELF_ERROR
@override(from: "...") references its own subgraph.
Use the name of the other subgraph.
OVERRIDE_SOURCE_HAS_OVERRIDE
Overridden field also has @override applied.
Only one subgraph can override a field at a time.
OVERRIDE_COLLISION_WITH_ANOTHER_DIRECTIVE
@override used with @external, @provides, or @requires.
Cannot override external or provided/required fields.
Type Merging
Types with the same name across subgraphs must be compatible.
FIELD_TYPE_MISMATCH
Same field has incompatible types across subgraphs.
Align types. Nullable fields can accept non-nullable, but not vice versa.
TYPE_KIND_MISMATCH
Same type name but different kinds (e.g., object vs interface).
Use consistent type definitions across subgraphs.
EMPTY_MERGED_ENUM_TYPE
Enum has no values common to all subgraphs.
Ensure at least one shared value, or use @inaccessible for subgraph-specific values.
Inaccessible
The @inaccessible directive hides elements from the API schema but has constraints.
REFERENCED_INACCESSIBLE
@inaccessible element referenced by a visible element.
Also mark the referencing element @inaccessible, or remove @inaccessible.
ONLY_INACCESSIBLE_CHILDREN
Type has only @inaccessible fields.
Add at least one accessible field to the type.
Satisfiability
SATISFIABILITY_ERROR
Query cannot be satisfied by available subgraphs. Common causes:
- Missing
@keyon entity - Missing shared key field between subgraphs
resolvable: falsewhen resolution is needed
Ensure a traversable path exists between subgraphs for every possible query.
Debugging Tips
- Run
rover supergraph compose --config supergraph.yamllocally - Check error codes in Apollo docs
- Use
rover subgraph checkto validate against production - Review
@keyfields are consistent across subgraphs - Verify all
@externalfields exist in originating subgraph