GraphQL diagnostics_channel tracing publishes non-structured-cloneable payloads in Cloudflare Workers Tail Workers
- Dominant language
- TypeScript
- Stars
- 20.3k
- Forks
- 2.1k
- Avg merge
- 44m
- Merged PRs (30d)
- 6
Description
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:
```js
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.
Contributor guide
Research direction
Start with graphql/diagnostics.mjs and resolveDiagnosticsChannel(), then reproduce the listed Cloudflare Worker setup with nodejs_compat, a Tail Worker, and a GraphQL parse/validate/execute request. Compare the tracing emission sites and the proposed opt-out, sanitization, and serialization approaches; done means GraphQL.js works without Cloudflare runtime diagnostics errors while useful instrumentation remains possible.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- Domain
- backend-api-design, observability-sre
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100