error_masking
The error_masking configuration controls how errors originating from your subgraphs are rewritten
before they are returned to clients. It lets you redact error messages and strip sensitive
extensions fields, globally or per subgraph.
For conceptual guidance and rollout strategy, see Error Masking.
Configuration Structure
Error masking is enabled by default. Even with no error_masking block, subgraph error messages
are replaced with "Unexpected error".
Options
enabled
- Type:
boolean - Default:
true
Master switch for the entire feature. When false, no masking is applied at all — both messages and
extensions pass through untouched, and every setting below is ignored. This can also be forced off
with the DISABLE_SUBGRAPH_ERROR_MASKING environment variable.
redacted_error_message
- Type:
string - Default:
"Unexpected error"
The message that replaces a subgraph error’s original message whenever masking is applied.
all
- Type:
object - Default:
{ enabled: true }
The default masking rules applied to every subgraph. Individual subgraphs can override these under
subgraphs.
all.enabled
- Type:
boolean - Default:
true
Whether to replace the error message with redacted_error_message.
Set to false to pass the original subgraph message through to clients.
all.extensions
- Type:
object - Default: unset (extensions are not masked)
Controls which keys of the error extensions object are kept or removed. When omitted, the router
leaves extensions untouched — only the message is masked.
all.extensions.mode
-
Type:
string -
Allowed values:
allow,deny -
Required: yes (when
extensionsis set) -
allow: an allowlist — only the keys inkeysare kept, everything else is removed.keys: []removes all extensions. -
deny: a denylist — only the keys inkeysare removed, everything else is kept.keys: []removes nothing.
all.extensions.keys
- Type:
string[] - Required: yes (when
extensionsis set)
The list of extensions keys the mode operates on. Only root-level keys are supported.
The router’s well-known extension keys are:
| Key | Description |
|---|---|
code | The error code (for example SUBREQUEST_HTTP_ERROR). |
service | The subgraph the error originated from. |
affectedPath | The response path affected by the error. |
Any other key a subgraph puts under extensions (for example http) is matched by its exact name.
subgraphs
- Type:
object(map of subgraph name → config) - Default: unset
Per-subgraph overrides. A subgraph listed here overrides the matching field from all.
subgraphs.<name>.enabled
- Type:
boolean - Default: inherits
all.enabled
Overrides message masking for this subgraph only. When omitted, the value from all is used.
subgraphs.<name>.extensions
- Type:
object - Default: unset
Overrides extensions masking for this subgraph only, using the same mode/keys shape as
all.extensions. When omitted, the subgraph inherits all.extensions.
Disabling via environment variable
Setting DISABLE_SUBGRAPH_ERROR_MASKING=true forces enabled to false, overriding
the config file. It is a convenient escape hatch for local debugging.
| Name | Accepted Values | Overrides (YAML) |
|---|---|---|
DISABLE_SUBGRAPH_ERROR_MASKING | true / false | error_masking.enabled |
Behavior
- Masking is the last step of the response pipeline, applied after metrics, tracing, and logging. Your observability tools always see the original, unmasked error — only the client-facing response is redacted.
- Only errors with an
extensions.servicefield are considered subgraph errors and are eligible for masking. This includes both errors returned by the subgraph and errors the router synthesizes for network/HTTP failures (SUBREQUEST_HTTP_ERROR,DOWNSTREAM_SERVICE_ERROR). - Message masking and extensions masking are independent: you can redact the message while keeping some extensions, or keep the message while stripping extensions.
- Masking applies to subscription errors too, including errors emitted mid-stream (for example
SUBGRAPH_SUBSCRIPTION_SSE_STREAM_ERROR).
Examples
Fully disable masking
Keep messages but mask nothing else
Pass original subgraph messages through while masking is otherwise active:
Custom redaction message
Mask the message and strip all extensions
Keep only the error code
Remove specific sensitive keys
Per-subgraph override
Mask everything globally, but trust the internal products subgraph enough to pass its original
messages and extensions through unmasked: