Skip to content

GraphQL diagnostics_channel tracing publishes non-structured-cloneable payloads in Cloudflare Workers Tail Workers #4834

Description

@astanciu

GraphQL.js 17 resolves node:diagnostics_channel at module load and creates graphql:* tracing channels.

In Cloudflare Workers with nodejs_compat and a Tail Worker attached, Cloudflare forwards diagnostics channel messages to Tail Workers as diagnosticsChannelEvents. That forwarding appears to require structured-cloneable payloads.

GraphQL diagnostic contexts include objects like GraphQLSchema, DocumentNode, Source, execution results, resolver args/results, etc. Some of these are not structured-cloneable. In production this causes Cloudflare runtime log errors like:

Failed to publish diagnostics channel message:

The request itself still succeeds; logs get polluted with runtime errors.

Reproduction shape

  1. Deploy a Cloudflare Worker with:
    • compatibility_flags = ["nodejs_compat"]
    • a Tail Worker configured
    • GraphQL.js 17
  2. Execute a GraphQL request through GraphQL Yoga or direct GraphQL.js parse/validate/execute.
  3. Runtime logs repeatedly show:

Failed to publish diagnostics channel message:

This happens even without application-level OpenTelemetry.

Relevant GraphQL.js behavior

graphql/diagnostics.mjs does:

const dc = resolveDiagnosticsChannel();
export const parseChannel = dc?.tracingChannel('graphql:parse');
export const validateChannel = dc?.tracingChannel('graphql:validate');

and emission sites trace when channel.hasSubscribers is true.

On Cloudflare, Tail forwarding appears to act as a diagnostics subscriber, so tracing is enabled even when the app did not intentionally subscribe to GraphQL diagnostics.

### Expected behavior

GraphQL.js 17 should work in Cloudflare Workers with Tail Workers enabled without runtime diagnostics errors.

Possible fixes:

- expose an official opt-out for GraphQL diagnostics tracing,
- avoid publishing non-structured-cloneable objects by default,
- provide serialization/sanitization hooks,
- or avoid enabling tracing for infrastructure-level diagnostics forwarding.

### Workaround

We patched resolveDiagnosticsChannel() to return undefined, disabling GraphQL diagnostics entirely. That works, but it is brittle and disables useful instrumentation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions