Join us for GraphQL Summit, October 10-12 in San Diego. Use promo code ODYSSEY for $400 off your pass.
Docs
Launch GraphOS Studio

Composition hints


When you successfully compose the s provided by your subgraphs into a , 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 Studio or via the Rover CLI.

Composition hints only appear in 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 . The rover supergraph compose command outputs rule violations for all local s.

See below for a list of composition rules categorized by rule type. The heading for each rule is the code that 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 (of a or definition) isn't present in all s and therefore won't be part of the .

Subgraph A
type Product {
id: ID!
name: String
price(currency: Currency): Float
}
Subgraph B
type Product {
id: ID!
name: String
price(currency: Currency, taxIncluded: Boolean): Float
}

Subgraph A
type Product {
id: ID!
name: String
price(currency: Currency, taxIncluded: Boolean): Float
}
Subgraph B
type Product {
id: ID!
name: String
price(currency: Currency, taxIncluded: Boolean): Float
}

INCONSISTENT_BUT_COMPATIBLE_ARGUMENT_TYPE

Indicates that an type (of a , input field, or definition) doesn't have the exact same type in all s, 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.

Subgraph A
type Product {
id: ID!
name: String
price(currency: Currency!): Float
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
Subgraph B
type Product {
id: ID!
name: String
price(currency: Currency): Float
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}

Subgraph A
type Product {
id: ID!
name: String
price(currency: Currency!): Float
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
Subgraph B
type Product {
id: ID!
name: String
price(currency: Currency!): Float
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}

INCONSISTENT_BUT_COMPATIBLE_FIELD_TYPE

Indicates that a doesn't have the exact same types in all s, 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.

Subgraph A
type Product {
id: ID!
name: String
price: Money
}
type Money {
amount: Float!
currency: Currency!
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
Subgraph B
type Product {
id: ID!
name: String
price: Money!
}
type Money {
amount: Float!
currency: Currency!
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}

Subgraph A
type Product {
id: ID!
name: String
price: Money!
}
type Money {
amount: Float!
currency: Currency!
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}
Subgraph B
type Product {
id: ID!
name: String
price: Money!
}
type Money {
amount: Float!
currency: Currency!
}
enum Currency {
USD
EUR
GBP
JPY
AUD
CAD
}

INCONSISTENT_DEFAULT_VALUE_PRESENCE

Indicates that an definition (of a , input field, or definition) has a default value in only some of the s that define the argument.

Subgraph A
type Product {
id: ID!
name: String
weight(kg: Float = 1.0): Float
}
Subgraph B
type Product {
id: ID!
name: String
weight(kg: Float): Float
}

Subgraph A
type Product {
id: ID!
name: String
weight(kg: Float = 1.0): Float
}
Subgraph B
type Product {
id: ID!
name: String
weight(kg: Float = 1.0): Float
}

INCONSISTENT_DESCRIPTION

Indicates that an element has a description in more than one , and the descriptions aren't equal.

Subgraph A
"""
A type representing a product.
"""
type Product {
id: ID!
name: String
}
Subgraph B
"""
An object representing a product.
"""
type Product {
id: ID!
name: String
}

Subgraph A
"""
A type representing a product.
"""
type Product {
id: ID!
name: String
}
Subgraph B
"""
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 s in which the object is defined.

Subgraph A
type Product @key(fields: "id") {
id: ID!
name: String
}
Subgraph B
type Product {
id: ID!
stock: Int
}

Subgraph A
type Product @key(fields: "id") {
id: ID!
name: String
}
Subgraph B
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 because it's defined in only some of the s that declare the enum.

Subgraph A
enum ProductStatus {
AVAILABLE
SOLD_OUT
BACK_ORDER
}
input ProductInput {
name: String!
status: ProductStatus!
}
Subgraph B
enum ProductStatus {
AVAILABLE
SOLD_OUT
}
input ProductInput {
name: String!
status: ProductStatus!
}

Subgraph A
enum ProductStatus {
AVAILABLE
SOLD_OUT
BACK_ORDER
}
input ProductInput {
name: String!
status: ProductStatus!
}
Subgraph B
enum ProductStatus {
AVAILABLE
SOLD_OUT
BACK_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 but is defined in only some of the s that declare the enum.

Subgraph A
enum OrderStatus {
CREATED
PROCESSING
COMPLETED
}
type Order {
name: String!
status: OrderStatus!
}
Subgraph B
enum OrderStatus {
CREATED
COMPLETED
}
type Order {
name: String!
status: OrderStatus!
}

Subgraph A
enum OrderStatus {
CREATED
PROCESSING
COMPLETED
}
type Order {
name: String!
status: OrderStatus!
}
Subgraph B
enum OrderStatus {
CREATED
PROCESSING
COMPLETED
}
type Order {
name: String!
status: OrderStatus!
}

INCONSISTENT_EXECUTABLE_DIRECTIVE_LOCATIONS

Indicates that an executable definition is declared with inconsistent locations across s. (The then uses the intersection of all locations in the supergraph.)

Subgraph A
directive @log(
message: String!
) on QUERY | FIELD
Subgraph B
directive @log(
message: String!
) on FIELD

Subgraph A
directive @log(
message: String!
) on QUERY | FIELD
Subgraph B
directive @log(
message: String!
) on QUERY | FIELD

INCONSISTENT_EXECUTABLE_DIRECTIVE_PRESENCE

Indicates that an executable definition is declared in only some of the s.

Subgraph A
directive @modify(
field: String!
) on FIELD
Subgraph B
# 🦗🦗🦗

Subgraph A
directive @modify(
field: String!
) on FIELD
Subgraph B
directive @modify(
field: String!
) on FIELD

INCONSISTENT_EXECUTABLE_DIRECTIVE_REPEATABLE

Indicates that an executable definition is marked repeatable in only some of the s and won't be repeatable in the .

Subgraph A
directive @validateLength(
max: Int!
) repeatable on FIELD
Subgraph B
directive @validateLength(
max: Int!
) on FIELD

Subgraph A
directive @validateLength(
max: Int!
) repeatable on FIELD
Subgraph B
directive @validateLength(
max: Int!
) repeatable on FIELD

INCONSISTENT_INPUT_OBJECT_FIELD

Indicates that a of an input definition is only defined in some of the s that declare the input object.

Subgraph A
input ProductInput {
name: String
price: Float
}
input OrderInput {
product: ProductInput
}
Subgraph B
input ProductInput {
name: String
}
input OrderInput {
product: ProductInput
}

Subgraph A
input ProductInput {
name: String
price: Float
}
input OrderInput {
product: ProductInput
}
Subgraph B
input ProductInput {
name: String
price: Float
}
input OrderInput {
product: ProductInput
}

INCONSISTENT_INTERFACE_VALUE_TYPE_FIELD

Indicates that a of an interface "value type" (has no @key in any ) isn't defined in all the subgraphs that declare the type.

Subgraph A
interface Product {
id: ID!
name: String
cost: Float
}
type DigitalProduct implements Product {
id: ID!
name: String
cost: Float
size: Int
}
Subgraph B
interface Product {
id: ID!
name: String
# cost is not defined in the interface
}
type PhysicalProduct implements Product {
id: ID!
name: String
cost: Float
weight: Float
}

Subgraph A
interface Product {
id: ID!
name: String
cost: Float
}
type DigitalProduct implements Product {
id: ID!
name: String
cost: Float
size: Int
}
Subgraph B
interface Product {
id: ID!
name: String
cost: Float
}
type PhysicalProduct implements Product {
id: ID!
name: String
cost: Float
weight: Float
}

INCONSISTENT_NON_REPEATABLE_DIRECTIVE_ARGUMENTS

A non-repeatable is applied to a element in different s but with s that are different.

Subgraph A
type Product {
id: ID!
name: String
}
type Query {
allProducts: [Product] @customDirective(orderBy: "name")
}
Subgraph B
type Product {
id: ID!
name: String
}
type Query {
allProducts: [Product] @customDirective(orderBy: "price")
}

Subgraph A
type Product {
id: ID!
name: String
}
type Query {
allProducts: [Product] @customDirective(orderBy: "name")
}
Subgraph B
type Product {
id: ID!
name: String
}
type Query {
allProducts: [Product] @customDirective(orderBy: "name")
}

INCONSISTENT_OBJECT_VALUE_TYPE_FIELD

Indicates that a of an object "value type" (has no @key in any ) isn't defined in all the subgraphs that declare the type.

Subgraph A
type Product {
id: ID!
name: String
price: Float
}
Subgraph B
type Product {
id: ID!
name: String
}

Subgraph A
type Product {
id: ID!
name: String
price: Float
}
Subgraph B
type Product {
id: ID!
name: String
price: Float
}

INCONSISTENT_RUNTIME_TYPES_FOR_SHAREABLE_RETURN

Indicates that a @shareable returns different sets of runtime types in the different s in which it's defined.

Subgraph A
type Product {
id: ID!
name: String
details: Details @shareable
}
type Details {
size: String
}
Subgraph B
type Product {
id: ID!
name: String
details: Details @shareable
}
type Details {
weight: Float
}

Subgraph A
type Product {
id: ID!
name: String
details: Details @shareable
}
type Details {
size: String
}
Subgraph B
type Product {
id: ID!
name: String
details: Details @shareable
}
type Details {
size: String
}

INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_LOCATIONS

Indicates that an executable definition is declared with inconsistent locations across s. (The then uses the intersection of all locations in the supergraph.)

Subgraph A
directive @customDirective(
message: String!
) on OBJECT | FIELD_DEFINITION
Subgraph B
directive @customDirective(
message: String!
) on FIELD_DEFINITION

Subgraph A
directive @customDirective(
message: String!
) on OBJECT | FIELD_DEFINITION
Subgraph B
directive @customDirective(
message: String!
) on OBJECT | FIELD_DEFINITION

INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_REPEATABLE

Indicates that a type system definition is marked repeatable in only some of the s that declare the directive and will be repeatable in the .

Subgraph A
directive @customDirective on OBJECT
Subgraph B
directive @customDirective repeatable on OBJECT

Subgraph A
directive @customDirective repeatable on OBJECT
Subgraph B
directive @customDirective repeatable on OBJECT

INCONSISTENT_UNION_MEMBER

Indicates that a member of a union type definition is only defined in some of the s that declare the union.

Subgraph A
type Product {
id: ID!
name: String
}
type Service {
id: ID!
description: String
}
union SearchResult = Product | Service
Subgraph B
type Product {
id: ID!
name: String
}
union SearchResult = Product

Subgraph A
type Product {
id: ID!
name: String
}
type Service {
id: ID!
description: String
}
union SearchResult = Product | Service
Subgraph B
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 with the @override no longer exists in a source , so the directive can be safely removed.

Subgraph A
type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Subgraph B")
}
Subgraph B
type Product @key(fields: "id") {
id: ID!
name: String!
}

Subgraph A
type Product @key(fields: "id") {
id: ID!
inStock: Boolean!
}
Subgraph B
type Product @key(fields: "id") {
id: ID!
name: String!
}

OVERRIDDEN_FIELD_CAN_BE_REMOVED

Indicates that a has been overridden by another . You should consider removing the overriden field to avoid confusion.

Subgraph A
type Product @key(fields: "id") {
id: ID!
name: String!
inStock: Boolean! @override(from: "Subgraph B")
}
Subgraph B
type Product @key(fields: "id") {
id: ID!
inStock: Boolean!
}

Subgraph A
type Product @key(fields: "id") {
id: ID!
name: String!
}
Subgraph B
type Product @key(fields: "id") {
id: ID!
inStock: Boolean!
}

UNUSED_ENUM_TYPE

Indicates that an enum type is defined in some s but is unused (no / references it). All values from subgraphs defining that enum will be included in the .

Subgraph A
enum ProductStatus {
AVAILABLE
SOLD_OUT
}
type Product {
id: ID!
name: String
}
Subgraph B
type Order {
id: ID!
product: Product
status: String
}

Subgraph A
type Product {
id: ID!
name: String
status: ProductStatus
}
Subgraph B
type Order {
id: ID!
product: Product
status: ProductStatus
}

Directives

DIRECTIVE_COMPOSITION

Indicates that an issue was detected when composing custom s.

MERGED_NON_REPEATABLE_DIRECTIVE_ARGUMENTS

Indicates a non-repeatable has been applied to a element in different s with different s and the arguments values were merged using the directive configured strategies.

Subgraph A
type Product {
id: ID!
name: String
}
type Query {
products: [Product] @customDirective(orderBy: ["name"])
}
Subgraph B
type Product {
id: ID!
name: String
}
type Query {
products: [Product] @customDirective(orderBy: ["price"])
}

Subgraph A
type Product {
id: ID!
name: String
}
type Query {
products: [Product] @customDirective(orderBy: ["name", "price"])
}
Subgraph B
type Product {
id: ID!
name: String
}
type Query {
products: [Product] @customDirective(orderBy: ["name", "price"])
}

NO_EXECUTABLE_DIRECTIVE_INTERSECTION

Indicates that an executable definition is declared with no shared locations across s.

Subgraph A
directive @log(
message: String!
) on QUERY
Subgraph B
directive @log(
message: String!
) on FIELD

Subgraph A
directive @log(
message: String!
) on QUERY | FIELD
Subgraph B
directive @log(
message: String!
) on QUERY | FIELD

FROM_SUBGRAPH_DOES_NOT_EXIST

Indicates the source specified by @override doesn't exist.

Subgraph A
type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Subgraph B")
}
Subgraph B
# Subgraph B doesn't exist

Subgraph A
type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Subgraph B")
}
Subgraph B
type Product @key(fields: "id") {
id: ID!
inStock: Boolean!
}
Previous
Error codes
Next
Federated trace data
Edit on GitHubEditForumsDiscord