-
Notifications
You must be signed in to change notification settings - Fork 236
Description
Bug description
posthog-node (via @posthog/core) triggers Cloudflare Workers cross-request promise resolution warnings because _flush() never consumes the fetch response body.
In packages/core/src/posthog-core-stateless.ts, _flush() calls fetchWithRetry() but discards the returned Response without reading the body (~line 1309):
await this.fetchWithRetry(url, fetchOptions, retryOptions)
// ↑ Response discarded — body never consumedfetchWithRetry() (~line 1367) returns the raw response without consuming it either:
res = await this.fetch(url, { signal: ..., ...options })
if (!isNoCors && (res.status < 200 || res.status >= 400)) {
throw new PostHogFetchHttpError(res, reqByteLength)
}
return res // body still unconsumedIn Cloudflare Workers, an unconsumed response body forces the runtime to clean up the underlying connection later, potentially in a different request's context, which triggers:
"Warning: A promise was resolved or rejected from a different request context than the one it was created in.
However, the creating request has already been completed or canceled. Continuations for that request are
unlikely to run safely and have been canceled."
This is the same class of bug the Sentry SDK had and fixed in getsentry/sentry-javascript#18534 / PR #18545.
Suggested fix: consume or cancel the response body in _flush():
// Option A: consume the body
const res = await this.fetchWithRetry(url, fetchOptions, retryOptions)
await res.text().catch(() => {})
// Option B: cancel the body stream
const res = await this.fetchWithRetry(url, fetchOptions, retryOptions)
await res.body?.cancel().catch(() => {})Workaround — pass a custom fetch that eagerly consumes response bodies:
const cfSafeFetch: typeof fetch = async (input, init) => {
const response = await fetch(input, init)
const body = await response.text().catch(() => '')
return new Response(body, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
})
}
new PostHog(apiKey, {
host,
flushAt: 1,
flushInterval: 0,
fetch: cfSafeFetch,
})How to reproduce
- Deploy a Cloudflare Worker that uses
posthog-nodewithflushAt: 1, flushInterval: 0(recommended config per PostHog CF Workers docs) - Call
capture()inside a request handler (e.g. viaexecutionContext.waitUntil(...)for non-blocking analytics) - Send concurrent requests to the worker — the runtime warns about cross-request promise resolution from the unconsumed response bodies left by
_flush()
Related sub-libraries
- All of them
- posthog-js (web)
- posthog-js-lite (web lite)
- posthog-node
- posthog-react-native
- @posthog/react
- @posthog/ai
- @posthog/convex
- @posthog/nextjs-config
- @posthog/nuxt
- @posthog/rollup-plugin
- @posthog/webpack-plugin
Additional context
posthog-node@5.24.11/@posthog/core@1.20.1- Cloudflare Workers with
compatibility_date: "2026-01-12"andnodejs_compatcompatibility flag - The warning is non-fatal but the runtime cancels continuations for the leaked promises, so post-flush error handling may silently not run
- High-traffic workers with analytics middleware on every request generate a high volume of these warnings
- The root cause is in
packages/core/src/posthog-core-stateless.ts, so it may affect any runtime that enforces response body consumption (not just CF Workers)