Hive RouterSecurity

Subgraph Error Masking

Error Masking prevents internal subgraph errors from leaking to clients. Subgraphs are internal services — the router sits between them and the outside world, so subgraph errors are rewritten into a safe, generic form before they reach clients.

Because the router masks on their behalf, your subgraphs don't need to sanitize their own errors: they can return rich, detailed errors for observability, and trust the router to redact them at the edge.

Enabled by default

Error masking is on by default. Out of the box, every subgraph error message is replaced with "Unexpected error", so no internal message reaches clients unless you opt out.

Why mask subgraph errors?

GraphQL errors from subgraphs frequently carry information that is useful internally but dangerous to expose publicly:

  • Sensitive detail leakage — database errors, stack traces, internal hostnames, or upstream HTTP response bodies embedded in an error message or its extensions.
  • Implementation disclosure — error codes and service names reveal your internal topology and the technologies each subgraph uses, giving attackers a map of your system.
  • Inconsistent client contract — different subgraphs phrase and structure errors differently. Masking gives clients a single, predictable error shape.

How it works

An error is treated as a subgraph error when it carries an extensions.service field. This covers two sources:

  1. Subgraph-returned errors — errors a subgraph includes in its GraphQL response.
  2. Router-synthesized errors — errors the router generates when a subgraph call fails at the network or HTTP layer (for example SUBREQUEST_HTTP_ERROR or DOWNSTREAM_SERVICE_ERROR).

Errors produced by the router itself that are unrelated to a subgraph — such as a GRAPHQL_PARSE_FAILED validation error — are never masked, since they carry no service field.

When masking applies, the router can:

  • Replace the error message with a fixed, safe string (default "Unexpected error").
  • Redact the error extensions via an allowlist or denylist of keys.

Both are independent and configurable globally or per subgraph.

Observability is unaffected

Masking runs last in the response pipeline — after metrics, tracing, and logging. Your telemetry always records the original, unmasked error; only the client-facing response is redacted.

What is masked by default

PartDefault behavior
messageReplaced with "Unexpected error".
extensionsNot touched — passed through as-is.

By default only the message is masked. Anything sensitive in extensions still reaches the client. If your subgraphs put internal detail in extensions, configure extensions masking as well — see the configuration reference.

Configuration

All settings live under the error_masking key. A few common setups:

Strictest — hide message and all extensions:

router.config.yaml
error_masking:
  all:
    error_message: true
    extensions:
      mode: allow
      keys: [] # allow none

Keep only the error code for client-side handling:

router.config.yaml
error_masking:
  all:
    error_message: true
    extensions:
      mode: allow
      keys:
        - code

Disable masking entirely (e.g. during development):

router.config.yaml
error_masking:
  all:
    error_message: false

Or via environment variable, without editing the config:

DISABLE_SUBGRAPH_ERROR_MASKING=true

See the error_masking configuration reference for the full set of options, per-subgraph overrides, and more examples.