Troubleshooting
Common errors and solutions when working with Apollo Connectors.
Table of Contents
- No + Operator for Concatenation
- Literal Values Require $()
- No Array Indexing Syntax
- MISSING_ENTITY_CONNECTOR Error
- INVALID_BODY Error
- No == Operator
- No Ternary Operator
- Filter with Multiple Conditions
- Circular Reference Error
- Headers Are Always Arrays
- Unnecessary Root $
- Wrong Federation/Connect Versions
- Entity Without Endpoint
- Selection vs GraphQL Response
- Batch Without API Support
No + Operator for Concatenation
Problem: Trying to concatenate strings with +.
# WRONG
fullName: firstName + " " + lastNameSolution: Use ->joinNotNull with an array.
# CORRECT
fullName: $([firstName, lastName])->joinNotNull(' ')
# With more parts
address: $([street, city, state, zip])->joinNotNull(', ')Literal Values Require $()
Problem: Literal values in mappings without the $() wrapper.
# WRONG - Will not compose
body: "{ userId: $args.id }"
greeting: "Hello"
count: 42Solution: Wrap literals in $().
# CORRECT
body: "$({ userId: $args.id })"
greeting: $("Hello")
count: $(42)
isActive: $(true)No Array Indexing Syntax
Problem: Trying to use bracket notation for array access.
# WRONG
firstItem: items[0]
thirdItem: items[2]Solution: Use array methods.
# CORRECT
firstItem: items->first
lastItem: items->last
thirdItem: items->get(2)
firstThree: items->slice(0, 3)MISSING_ENTITY_CONNECTOR Error
Problem: An entity type is missing a @connect directive.
Error: MISSING_ENTITY_CONNECTOR
Entity "User" is missing a connector.Cause: You've referenced a type as an entity (via stub or @key) but haven't defined how to resolve it.
Solution: Add @connect to the entity type.
# Add @connect to make it a resolvable entity
type User @connect(
source: "api"
http: { GET: "/users/{$this.id}" }
selection: "id name email"
) {
id: ID!
name: String
email: String
}Or, if it shouldn't be an entity, remove the entity stub and inline the data.
INVALID_BODY Error
Problem: Request body not properly formatted.
Error: INVALID_BODY
Body must use literal syntax.Cause: Object literal in body without $() wrapper.
# WRONG
body: "{ userId: $args.id }"Solution: Use $() for object literals.
# CORRECT
body: "$({ userId: $args.id })"
# For nested objects
body: "$({ user: { id: $args.id, name: $args.name } })"No == Operator
Problem: Using == for equality comparison.
# WRONG
isActive: status == "active"
is200: $status == 200Solution: Use the ->eq method.
# CORRECT
isActive: status->eq("active")
is200: $status->eq(200)For HTTP status checks with simple boolean returns:
# For DELETE operations that should return true on success
selection: "$(true)"No Ternary Operator
Problem: Using ternary operator for conditional values.
# WRONG
result: condition ? valueA : valueBSolution: Use null coalescing operators.
# Use ?? for null/undefined fallback
result: value ?? "default"
# Use ?! for undefined-only fallback (preserves null)
result: value ?! "fallback"
# Use ->match for value mapping
result: status->match(
["active", "Active User"],
["inactive", "Inactive User"],
[@, "Unknown"]
)Filter with Multiple Conditions
Problem: Using ->and inside filter or find.
# WRONG - @ changes meaning in nested method
items: $.list->filter(@.active->and(@.price->gt(10)))Cause: The @ variable refers to different values in nested contexts.
Solution: Chain filter calls.
# CORRECT - Chain filters
items: $.list->filter(@.active)->filter(@.price->gt(10))
# For find
item: $.list->filter(@.active)->find(@.price->gt(10))Circular Reference Error
Problem: Entity stubs create a circular dependency.
Error: Circular reference detected between Product and ReviewCause: Product has reviews, Review has product, both as entity stubs.
Solution: Use @inaccessible foreign key pattern.
type Product {
id: ID!
reviews: [Review] @connect(
http: { GET: "/products/{$this.id}/reviews" }
selection: "id rating text productId" # Include FK
)
}
type Review {
id: ID!
rating: Int
text: String
productId: ID! @inaccessible # Hide from clients
product: Product @connect(
http: { GET: "/products/{$this.productId}" }
selection: "id name"
)
}Headers Are Always Arrays
Problem: Accessing header value directly.
# WRONG - Headers are arrays
auth: $request.headers.authorizationSolution: Use ->first to get the first value.
# CORRECT
auth: $request.headers.authorization->first
# For headers with special characters
custom: $request.headers.'x-custom-header'->firstUnnecessary Root $
Problem: Using $ when selecting from root.
# WRONG - Unnecessary
selection: """
$ {
id
name
}
"""Solution: Select fields directly.
# CORRECT
selection: """
id
name
"""Use $ only when:
- Accessing a nested path:
$.results { id } - Using a method:
$->first { id }
Wrong Federation/Connect Versions
Problem: Schema won't compose due to version mismatch.
Error: Incompatible federation and connect versionsSolution: Use the latest LTS Federation version together with the latest generally available Connectors spec that LTS supports. Use an experimental Connectors spec only when the user explicitly asks for it.
# CORRECT - current LTS; confirm against the docs before copying
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.15")
@link(url: "https://specs.apollo.dev/connect/v0.3", import: ["@source", "@connect"])See Version requirements for how to resolve the current LTS. Pin federation_version in supergraph.yaml to the latest patch of that Federation LTS (currently =2.15.2).
Entity Without Endpoint
Problem: Want to create an entity but there's no API endpoint for it.
Solution: Don't make it an entity. Use a regular type.
# If there's no /address/{id} endpoint, don't make it an entity
type Address { # No @connect, no @key
street: String
city: String
country: String
}
# Include it inline in the parent's selection
type User @connect(
http: { GET: "/users/{$this.id}" }
selection: """
id
name
address {
street
city
country
}
"""
) {
id: ID!
name: String
address: Address
}Selection vs GraphQL Response
Problem: Test expectations don't match actual output.
Cause: connectorResponse in tests is the selection mapping result, not the final GraphQL response.
# apiResponseBody from REST API
apiResponseBody: |
{ "user_id": "123", "user_name": "Alice" }
# Selection mapping
selection: """
id: user_id
name: user_name
"""
# connectorResponse is the mapping result
connectorResponse: |
{ "id": "123", "name": "Alice" }Note: No type conversion unless explicitly done with ->parseInt, ->toString, etc.
Batch Without API Support
Problem: Using $batch but API doesn't support batch requests.
Solution:
- Ask user if API has a batch endpoint
- If not, use non-batch pattern and accept N+1 queries
- Consider if the API can be modified to add batch support
# Non-batch (N+1)
type Product @connect(
http: { GET: "/products/{$this.id}" }
selection: "id name"
) {
id: ID!
name: String
}
# Batch (requires API support)
type Product @connect(
http: {
POST: "/products/batch"
body: "ids: $batch.id"
}
selection: "id name"
) {
id: ID!
name: String
}