Subgraph Error Inclusion

Configure the router to propagate subgraph errors to clients


By default, a GraphOS Router or Apollo Router Core redacts the details of subgraph errors in responses to clients. The router instead returns a default error with the following message:

Text
1Subgraph errors redacted

This redaction prevents potential leaks of sensitive information to the client. Using the include_subgraph_errors plugin, you can configure the router to propagate subgraph errors to clients instead. You can do this for all subgraphs, or on a per-subgraph basis.

Configuration

To configure subgraph error inclusion, add the include_subgraph_errors plugin to your YAML config file, like so:

YAML
router.yaml
1# Option 1: Simple boolean toggle (default is false)
2include_subgraph_errors:
3  all: true # Propagate errors (message + extensions) from all subgraphs
4  subgraphs:
5    products: false # Override: Do not propagate errors from the 'products' subgraph (redact fully)

Any configuration under the subgraphs key takes precedence over the all configuration for that specific subgraph. In the example above, subgraph errors are included from all subgraphs except the products subgraph, which will have its errors fully redacted.

If all is a boolean (true or false), then any configuration under subgraphs must also be a boolean.

YAML
router.yaml
1# Option 2: Fine-grained control using objects
2include_subgraph_errors:
3  all: # Default configuration for all subgraphs
4    redact_message: true # Redact error messages globally
5    allow_extensions_keys: # Allow only specific extension keys globally
6      - code
7      - trace_id
8  subgraphs:
9    # Subgraph 'products': Override global settings
10    products:
11      redact_message: false # Keep original error messages for 'products'
12      allow_extensions_keys: # Extend global allow list for 'products'
13        - reason # Allows 'code', 'trace_id' (from global) and 'reason'
14      exclude_global_keys: # Exclude 'trace_id' from the inherited global list
15        - trace_id # Allows 'code' (global) and 'reason' (subgraph), but not 'trace_id'
16
17    # Subgraph 'inventory': Override global allow list with a deny list
18    inventory:
19      deny_extensions_keys: # Deny specific keys for 'inventory' (overrides global allow list)
20        - internal_debug_info
21        # Allows 'code', 'trace_id' (from global) but denies 'internal_debug_info'
22
23    # Subgraph 'reviews': Use only common options, inheriting global allow/deny behavior
24    reviews:
25      redact_message: false # Override only message redaction, inherits global allow list
26      exclude_global_keys: # Inherits global allow list, but excludes 'code'
27        - code # Allows 'trace_id' but not 'code'
28
29    # Subgraph 'accounts': Fully redact errors, overriding global object config
30    accounts: false
note
Using a deny_extensions_keys approach carries security risks because it follows a blocklist pattern—any sensitive information not explicitly included in the deny list might be exposed to clients if not covered by other rules (like a global allow_extensions_keys).

For better security, we recommend either fully redacting subgraph errors (by setting the subgraph to false) or using the allow_extensions_keys approach (either globally or per-subgraph) to explicitly specify which error extension fields can be exposed to clients.

Configuration Schema

The top-level include_subgraph_errors key accepts an object with the following keys:

KeyTypeDescriptionDefault
allboolean | ErrorMode ObjectConfiguration applied to all subgraphs unless overridden.false
subgraphsmap<string, boolean | ErrorMode Object>Per-subgraph overrides for the all configuration. The key is the subgraph name.{}

ErrorMode Object

This object provides fine-grained control over error propagation.

KeyTypeDescriptionRequired
redact_messagebooleanIf true, replaces the original error message with Subgraph errors redacted. If false, keeps the original message.Optional
allow_extensions_keys[string]Propagates only the specified keys in the extensions object. Cannot be used with deny_extensions_keys in the same object. If omitted, inherits global behavior.Optional
deny_extensions_keys[string]Redacts the specified keys from the extensions object. Cannot be used with allow_extensions_keys in the same object. If omitted, inherits global behavior.Optional
exclude_global_keys[string]When inheriting a global allow_extensions_keys or deny_extensions_keys list, these keys are removed from the inherited list before applying subgraph-specific rules.Optional

Key Behaviors & Precedence

  1. Subgraph Specificity: Configuration under subgraphs.<subgraph_name> always overrides the all configuration for that specific subgraph.

  2. Boolean Override: If subgraphs.<subgraph_name> is set to true or false, it completely overrides any all object configuration.

    • true: Include the error, keep the original message, include all extensions (except service if explicitly denied later, though unlikely with true).

    • false: Redact the error message and remove all extensions.

  3. Global Boolean Restriction: If all is set to true or false, then all entries under subgraphs must also be true or false. Object configurations are not allowed for subgraphs in this case.

  4. Allow vs. Deny: Within a single configuration object (either all or a specific subgraph), allow_extensions_keys and deny_extensions_keys are mutually exclusive.

  5. Inheritance & Overrides (Object Config):

    • If a subgraph config is an object, it inherits the behavior (allow or deny list, redact_message) from the global all object config by default.

    • redact_message in the subgraph object overrides the global redact_message.

    • allow_extensions_keys in the subgraph object:

      • Overrides a global deny_extensions_keys list.

      • Extends a global allow_extensions_keys list (after applying exclude_global_keys).

    • deny_extensions_keys in the subgraph object:

      • Overrides a global allow_extensions_keys list.

      • Extends a global deny_extensions_keys list (after applying exclude_global_keys).

    • exclude_global_keys removes keys from the inherited global list before the subgraph's allow or deny list is applied or extended.

  6. service Extension: The service extension (containing the subgraph name) is added by default if errors are included for a subgraph, unless it's explicitly removed by an allow_extensions_keys list (that doesn't include "service") or a deny_extensions_keys list (that includes "service").

Sending errors to GraphOS

Reporting subgraph errors to GraphOS is configured separately and is not affected by client-facing error inclusion settings. See the GraphOS reporting docs.

Logging GraphQL request errors

To log the GraphQL error responses (i.e., messages returned in the GraphQL errors array) from the router, see the logging configuration documentation.

Exposing subgraph name via service extension

If errors are included for a particular subgraph (i.e., not fully redacted by setting its config to false), the router attempts to add the subgraph's name to the error's extensions object under the key service.

This service extension key is treated like any other extension key and is subject to the allow_extensions_keys and deny_extensions_keys rules.

  • If using allow_extensions_keys, you must include "service" in the list if you want it to be propagated.

  • If using deny_extensions_keys, including "service" will prevent it from being propagated.

  • If no allow/deny lists apply (e.g., all: true), "service" will be included by default.

Example:

Assume include_subgraph_errors.all is configured as:

YAML
1all:
2  redact_message: false
3  allow_extensions_keys:
4    - code # Allows only 'code', implicitly denying 'service'

If the products subgraph returns an error like {"message": "Invalid ID", "extensions": {"code": "BAD_USER_INPUT"}}, the final error sent to the client will be:

JSON
1{
2  "message": "Invalid ID",
3  "path": [...],
4  "extensions": {
5    "code": "BAD_USER_INPUT"
6    // "service": "products" is NOT included because it wasn't in allow_extensions_keys
7  }
8}

If the configuration was instead:

YAML
1all:
2  redact_message: false
3  allow_extensions_keys:
4    - code
5    - service # Explicitly allow 'service'

The final error would be:

JSON
1{
2  "data": null,
3  "errors": [
4    {
5      "message": "Invalid product ID",
6      "path": [],
7      "extensions": {
8        "service": "products",
9      }
10    }
11  ]
12}
Feedback

Edit on GitHub

Forums