Composition hints
When you successfully compose the schemas provided by your subgraphs into a supergraph schema, the composition process can flag potential improvements or hints. Hints are violations of the GraphOS schema linter's composition rules. You can review them on the Checks page in GraphOS Studio or via the Rover CLI.
Composition hints only appear in GraphOS Studio and via the rover subgraph check
command for graphs on federation version 2.4
or later. You can update a graph's version from its Settings page in GraphOS Studio.
The rover subgraph check
command outputs rule violations with the severity levels you've configured for your graph variant. The rover supergraph compose
command outputs rule violations for all local subgraph schemas.
See below for a list of composition rules categorized by rule type. The heading for each rule is the code that GraphOS returns for the rule violation. Refer to the rules reference page for a comprehensive list of linter rules.
Inconsistent elements
INCONSISTENT_ARGUMENT_PRESENCE
Indicates that an optional argument (of a field or directive definition) isn't present in all subgraphs and therefore won't be part of the supergraph.
❌
type Product {id: ID!name: Stringprice(currency: Currency): Float}
type Product {id: ID!name: Stringprice(currency: Currency, taxIncluded: Boolean): Float}
✅
type Product {id: ID!name: Stringprice(currency: Currency, taxIncluded: Boolean): Float}
type Product {id: ID!name: Stringprice(currency: Currency, taxIncluded: Boolean): Float}
INCONSISTENT_BUT_COMPATIBLE_ARGUMENT_TYPE
Indicates that an argument type (of a field, input field, or directive definition) doesn't have the exact same type in all subgraphs, but that the types are compatible.
Two types are compatible if one is:
- a non-nullable version
- a list version
- a subtype
- or a combination of any of these
of the other.
❌
type Product {id: ID!name: Stringprice(currency: Currency!): Float}enum Currency {USDEURGBPJPYAUDCAD}
type Product {id: ID!name: Stringprice(currency: Currency): Float}enum Currency {USDEURGBPJPYAUDCAD}
✅
type Product {id: ID!name: Stringprice(currency: Currency!): Float}enum Currency {USDEURGBPJPYAUDCAD}
type Product {id: ID!name: Stringprice(currency: Currency!): Float}enum Currency {USDEURGBPJPYAUDCAD}
INCONSISTENT_BUT_COMPATIBLE_FIELD_TYPE
Indicates that a field doesn't have the exact same types in all subgraphs, but that the types are compatible.
Two types are compatible if one is:
- a non-nullable version
- a list version
- a subtype
- or a combination of any of these
of the other.
❌
type Product {id: ID!name: Stringprice: Money}type Money {amount: Float!currency: Currency!}enum Currency {USDEURGBPJPYAUDCAD}
type Product {id: ID!name: Stringprice: Money!}type Money {amount: Float!currency: Currency!}enum Currency {USDEURGBPJPYAUDCAD}
✅
type Product {id: ID!name: Stringprice: Money!}type Money {amount: Float!currency: Currency!}enum Currency {USDEURGBPJPYAUDCAD}
type Product {id: ID!name: Stringprice: Money!}type Money {amount: Float!currency: Currency!}enum Currency {USDEURGBPJPYAUDCAD}
INCONSISTENT_DEFAULT_VALUE_PRESENCE
Indicates that an argument definition (of a field, input field, or directive definition) has a default value in only some of the subgraphs that define the argument.
❌
type Product {id: ID!name: Stringweight(kg: Float = 1.0): Float}
type Product {id: ID!name: Stringweight(kg: Float): Float}
✅
type Product {id: ID!name: Stringweight(kg: Float = 1.0): Float}
type Product {id: ID!name: Stringweight(kg: Float = 1.0): Float}
INCONSISTENT_DESCRIPTION
Indicates that an element has a description in more than one subgraph, and the descriptions aren't equal.
❌
"""A type representing a product."""type Product {id: ID!name: String}
"""An object representing a product."""type Product {id: ID!name: String}
✅
"""A type representing a product."""type Product {id: ID!name: String}
"""A type representing a product."""type Product {id: ID!name: String}
INCONSISTENT_ENTITY
Indicates that an object is declared as an entity (has a @key
) in only some of the subgraphs in which the object is defined.
❌
type Product @key(fields: "id") {id: ID!name: String}
type Product {id: ID!stock: Int}
✅
type Product @key(fields: "id") {id: ID!name: String}
type Product @key(fields: "id") {id: ID!stock: Int}
INCONSISTENT_ENUM_VALUE_FOR_INPUT_ENUM
Indicates that a value of an enum type definition, that is only used as an input type, hasn't been merged into the supergraph because it's defined in only some of the subgraphs that declare the enum.
❌
enum ProductStatus {AVAILABLESOLD_OUTBACK_ORDER}input ProductInput {name: String!status: ProductStatus!}
enum ProductStatus {AVAILABLESOLD_OUT}input ProductInput {name: String!status: ProductStatus!}
✅
enum ProductStatus {AVAILABLESOLD_OUTBACK_ORDER}input ProductInput {name: String!status: ProductStatus!}
enum ProductStatus {AVAILABLESOLD_OUTBACK_ORDER}input ProductInput {name: String!status: ProductStatus!}
INCONSISTENT_ENUM_VALUE_FOR_OUTPUT_ENUM
Indicates that a value of an enum type definition, that is only used as an output type or is unused, has been merged in the supergraph but is defined in only some of the subgraphs that declare the enum.
❌
enum OrderStatus {CREATEDPROCESSINGCOMPLETED}type Order {name: String!status: OrderStatus!}
enum OrderStatus {CREATEDCOMPLETED}type Order {name: String!status: OrderStatus!}
✅
enum OrderStatus {CREATEDPROCESSINGCOMPLETED}type Order {name: String!status: OrderStatus!}
enum OrderStatus {CREATEDPROCESSINGCOMPLETED}type Order {name: String!status: OrderStatus!}
INCONSISTENT_EXECUTABLE_DIRECTIVE_LOCATIONS
Indicates that an executable directive definition is declared with inconsistent locations across subgraphs. (The supergraph schema then uses the intersection of all locations in the supergraph.)
❌
directive @log(message: String!) on QUERY | FIELD
directive @log(message: String!) on FIELD
✅
directive @log(message: String!) on QUERY | FIELD
directive @log(message: String!) on QUERY | FIELD
INCONSISTENT_EXECUTABLE_DIRECTIVE_PRESENCE
Indicates that an executable directive definition is declared in only some of the subgraphs.
❌
directive @modify(field: String!) on FIELD
# 🦗🦗🦗
✅
directive @modify(field: String!) on FIELD
directive @modify(field: String!) on FIELD
INCONSISTENT_EXECUTABLE_DIRECTIVE_REPEATABLE
Indicates that an executable directive definition is marked repeatable in only some of the subgraphs and won't be repeatable in the supergraph.
❌
directive @validateLength(max: Int!) repeatable on FIELD
directive @validateLength(max: Int!) on FIELD
✅
directive @validateLength(max: Int!) repeatable on FIELD
directive @validateLength(max: Int!) repeatable on FIELD
INCONSISTENT_INPUT_OBJECT_FIELD
Indicates that a field of an input object type definition is only defined in some of the subgraphs that declare the input object.
❌
input ProductInput {name: Stringprice: Float}input OrderInput {product: ProductInput}
input ProductInput {name: String}input OrderInput {product: ProductInput}
✅
input ProductInput {name: Stringprice: Float}input OrderInput {product: ProductInput}
input ProductInput {name: Stringprice: Float}input OrderInput {product: ProductInput}
INCONSISTENT_INTERFACE_VALUE_TYPE_FIELD
Indicates that a field of an interface "value type" (has no @key
in any subgraph) isn't defined in all the subgraphs that declare the type.
❌
interface Product {id: ID!name: Stringcost: Float}type DigitalProduct implements Product {id: ID!name: Stringcost: Floatsize: Int}
interface Product {id: ID!name: String# cost is not defined in the interface}type PhysicalProduct implements Product {id: ID!name: Stringcost: Floatweight: Float}
✅
interface Product {id: ID!name: Stringcost: Float}type DigitalProduct implements Product {id: ID!name: Stringcost: Floatsize: Int}
interface Product {id: ID!name: Stringcost: Float}type PhysicalProduct implements Product {id: ID!name: Stringcost: Floatweight: Float}
INCONSISTENT_NON_REPEATABLE_DIRECTIVE_ARGUMENTS
A non-repeatable directive is applied to a schema element in different subgraphs but with arguments that are different.
❌
type Product {id: ID!name: String}type Query {allProducts: [Product] @customDirective(orderBy: "name")}
type Product {id: ID!name: String}type Query {allProducts: [Product] @customDirective(orderBy: "price")}
✅
type Product {id: ID!name: String}type Query {allProducts: [Product] @customDirective(orderBy: "name")}
type Product {id: ID!name: String}type Query {allProducts: [Product] @customDirective(orderBy: "name")}
INCONSISTENT_OBJECT_VALUE_TYPE_FIELD
Indicates that a field of an object "value type" (has no @key
in any subgraph) isn't defined in all the subgraphs that declare the type.
❌
type Product {id: ID!name: Stringprice: Float}
type Product {id: ID!name: String}
✅
type Product {id: ID!name: Stringprice: Float}
type Product {id: ID!name: Stringprice: Float}
INCONSISTENT_RUNTIME_TYPES_FOR_SHAREABLE_RETURN
Indicates that a @shareable field returns different sets of runtime types in the different subgraphs in which it's defined.
❌
type Product {id: ID!name: Stringdetails: Details @shareable}type Details {size: String}
type Product {id: ID!name: Stringdetails: Details @shareable}type Details {weight: Float}
✅
type Product {id: ID!name: Stringdetails: Details @shareable}type Details {size: String}
type Product {id: ID!name: Stringdetails: Details @shareable}type Details {size: String}
INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_LOCATIONS
Indicates that an executable directive definition is declared with inconsistent locations across subgraphs. (The supergraph schema then uses the intersection of all locations in the supergraph.)
❌
directive @customDirective(message: String!) on OBJECT | FIELD_DEFINITION
directive @customDirective(message: String!) on FIELD_DEFINITION
✅
directive @customDirective(message: String!) on OBJECT | FIELD_DEFINITION
directive @customDirective(message: String!) on OBJECT | FIELD_DEFINITION
INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_REPEATABLE
Indicates that a type system directive definition is marked repeatable in only some of the subgraphs that declare the directive and will be repeatable in the supergraph.
❌
directive @customDirective on OBJECT
directive @customDirective repeatable on OBJECT
✅
directive @customDirective repeatable on OBJECT
directive @customDirective repeatable on OBJECT
INCONSISTENT_UNION_MEMBER
Indicates that a member of a union type definition is only defined in some of the subgraphs that declare the union.
❌
type Product {id: ID!name: String}type Service {id: ID!description: String}union SearchResult = Product | Service
type Product {id: ID!name: String}union SearchResult = Product
✅
type Product {id: ID!name: String}type Service {id: ID!description: String}union SearchResult = Product | Service
type Product {id: ID!name: String}type Service {id: ID!description: String}union SearchResult = Product | Service
Overridden and unused elements
OVERRIDE_DIRECTIVE_CAN_BE_REMOVED
Indicates a field with the @override directive no longer exists in a source subgraph, so the directive can be safely removed.
❌
type Product @key(fields: "id") {id: ID!inStock: Boolean! @override(from: "Subgraph B")}
type Product @key(fields: "id") {id: ID!name: String!}
✅
type Product @key(fields: "id") {id: ID!inStock: Boolean!}
type Product @key(fields: "id") {id: ID!name: String!}
OVERRIDDEN_FIELD_CAN_BE_REMOVED
Indicates that a field has been overridden by another subgraph. You should consider removing the overriden field to avoid confusion.
❌
type Product @key(fields: "id") {id: ID!name: String!inStock: Boolean! @override(from: "Subgraph B")}
type Product @key(fields: "id") {id: ID!inStock: Boolean!}
✅
type Product @key(fields: "id") {id: ID!name: String!}
type Product @key(fields: "id") {id: ID!inStock: Boolean!}
UNUSED_ENUM_TYPE
Indicates that an enum type is defined in some subgraphs but is unused (no field/argument references it). All values from subgraphs defining that enum will be included in the supergraph.
❌
enum ProductStatus {AVAILABLESOLD_OUT}type Product {id: ID!name: String}
type Order {id: ID!product: Productstatus: String}
✅
type Product {id: ID!name: Stringstatus: ProductStatus}
type Order {id: ID!product: Productstatus: ProductStatus}
Directives
DIRECTIVE_COMPOSITION
Indicates that an issue was detected when composing custom directives.
MERGED_NON_REPEATABLE_DIRECTIVE_ARGUMENTS
Indicates a non-repeatable directive has been applied to a schema element in different subgraphs with different arguments and the arguments values were merged using the directive configured strategies.
❌
type Product {id: ID!name: String}type Query {products: [Product] @customDirective(orderBy: ["name"])}
type Product {id: ID!name: String}type Query {products: [Product] @customDirective(orderBy: ["price"])}
✅
type Product {id: ID!name: String}type Query {products: [Product] @customDirective(orderBy: ["name", "price"])}
type Product {id: ID!name: String}type Query {products: [Product] @customDirective(orderBy: ["name", "price"])}
NO_EXECUTABLE_DIRECTIVE_INTERSECTION
Indicates that an executable directive definition is declared with no shared locations across subgraphs.
❌
directive @log(message: String!) on QUERY
directive @log(message: String!) on FIELD
✅
directive @log(message: String!) on QUERY | FIELD
directive @log(message: String!) on QUERY | FIELD
FROM_SUBGRAPH_DOES_NOT_EXIST
Indicates the source subgraph specified by @override
directive doesn't exist.
❌
type Product @key(fields: "id") {id: ID!inStock: Boolean! @override(from: "Subgraph B")}
# Subgraph B doesn't exist
✅
type Product @key(fields: "id") {id: ID!inStock: Boolean! @override(from: "Subgraph B")}
type Product @key(fields: "id") {id: ID!inStock: Boolean!}