posthog-node
semver
>=3.0.0postconditions10functions9last verified2026-04-17coverage score100%Postconditions — what we check
- getFeatureFlag · network-errorerrorWhenNetwork failure, DNS resolution failure, PostHog API unreachable, API key invalid, rate limit exceeded, or any server-side error (5xx)Throws
Error or unhandled Promise rejection with network/HTTP details. PostHog SDK does not define a typed error class for feature flag calls; the rejection is a generic Error with message describing the failure.Required handlingCaller MUST wrap in try-catch (or use .catch()). Unlike capture()/identify() which are fire-and-forget, getFeatureFlag() makes a live network call and WILL reject on failure. Uncaught rejections crash the request handler or produce unhandled rejection warnings in Node.js. Minimum handling: try { const flag = await posthog.getFeatureFlag('my-flag', userId); if (flag === true) { /* feature on */ } } catch (err) { console.error('Feature flag check failed:', err); // Fall back to default behavior }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - isFeatureEnabled · network-errorerrorWhenNetwork failure, DNS resolution failure, PostHog API unreachable, or server-side errorThrows
Error or unhandled Promise rejection. Same error surface as getFeatureFlag().Required handlingCaller MUST wrap in try-catch. isFeatureEnabled() makes a live network call and rejects on failure. Missing error handling causes unhandled rejection. Minimum handling: try { const enabled = await posthog.isFeatureEnabled('my-flag', userId); if (enabled) { /* ... */ } } catch (err) { console.error('Feature flag check failed:', err); // Fall back to default (disabled) }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - getAllFlags · network-errorerrorWhenNetwork failure, PostHog API unreachable, or server-side errorThrows
Error or unhandled Promise rejection.Required handlingCaller MUST wrap in try-catch. getAllFlags() makes a live network call and rejects on failure. This is the most common source of unhandled PostHog rejections in production (confirmed in n8n codebase). Minimum handling: try { const flags = await posthog.getAllFlags(userId); return flags; } catch (err) { console.error('Failed to fetch feature flags:', err); return {}; // Safe default: all flags disabled }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - shutdown · shutdown-timeout-errorwarningWhenFlush does not complete within shutdownTimeoutMs (default 30 seconds) — all pending events cannot be delivered before the timeout fires.Throws
String rejection: 'Timeout while shutting down PostHog. Some events may not have been sent.' Note: This is a string, not an Error instance — typeof err === 'string'. In Node.js, unhandled string rejections still trigger unhandledRejection events.Required handlingCaller SHOULD wrap in try-catch. Shutdown timeout means queued analytics events are lost — not a business-critical failure, but unhandled rejections interfere with process exit in Node.js and signal graceful shutdown success to orchestrators. Recommended handling: try { await posthog.shutdown(); } catch (err) { console.error('PostHog shutdown timed out, some events may be lost:', err); // Non-critical — continue with process exit }costlowin prodimmediate exceptionusers seedegraded performancevisibilitysilentSources[1] - getAllFlagsAndPayloads · get-all-flags-and-payloads-network-errorerrorWhenNetwork failure, DNS resolution failure, PostHog API unreachable, API key invalid, rate limit exceeded, or any server-side error (5xx) when fetching all flags and payloads in a single batch call.Throws
PostHogFetchHttpError — thrown on HTTP error responses (non-2xx status codes). Properties: .response (Response object), .name === 'PostHogFetchHttpError'. PostHogFetchNetworkError — thrown on network/fetch failures. Properties: .error (underlying Error), .name === 'PostHogFetchNetworkError'. Both are exported from @posthog/core (dependency of posthog-node).Required handlingCaller MUST wrap in try-catch. getAllFlagsAndPayloads() makes a live network call and rejects on failure. Particularly dangerous in initialization code where flag payloads drive configuration — uncaught rejection crashes the entire startup flow or request handler. Minimum handling: try { const { featureFlags, featureFlagPayloads } = await posthog.getAllFlagsAndPayloads(userId); const config = featureFlagPayloads['ui-config']; return { flags: featureFlags, config }; } catch (err) { console.error('Failed to fetch flags and payloads:', err); return { flags: {}, config: null }; // Safe default }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - getFeatureFlagPayload · get-feature-flag-payload-network-errorerrorWhenNetwork failure, DNS resolution failure, PostHog API unreachable, API key invalid, rate limit exceeded, or server-side error during flag payload fetch.Throws
PostHogFetchHttpError or PostHogFetchNetworkError (from @posthog/core). Same error types as getFeatureFlag(). Note: returns undefined (not throws) when the flag exists but has no payload — only network/auth failures throw.Required handlingCaller MUST wrap in try-catch. Payload failures are particularly dangerous because callers often use payloads for dynamic configuration — an uncaught rejection means configuration loading fails entirely, potentially crashing the service or leaving it with no configuration at all. Minimum handling: try { const payload = await posthog.getFeatureFlagPayload('ui-config', userId); const config = payload as { theme: string } ?? { theme: 'default' }; return config; } catch (err) { console.error('Failed to fetch flag payload:', err); return { theme: 'default' }; // Fall back to default config }costmediumin prodimmediate exceptionusers seedegraded performancevisibilitysilentSources[1] - getFeatureFlagResult · get-feature-flag-result-network-errorerrorWhenNetwork failure, PostHog API unreachable, API key invalid, rate limit exceeded, or server-side error during the combined flag+payload fetch.Throws
PostHogFetchHttpError or PostHogFetchNetworkError (from @posthog/core). Same error types as getFeatureFlag() and getFeatureFlagPayload().Required handlingCaller MUST wrap in try-catch. getFeatureFlagResult() makes a live network call when local evaluation is unavailable. The combined nature of the result means both feature gating and configuration data are unavailable on failure. Minimum handling: try { const result = await posthog.getFeatureFlagResult('checkout-v2', userId); if (result?.enabled) { return result.payload as CheckoutConfig ?? defaultConfig; } return defaultConfig; } catch (err) { console.error('Feature flag result fetch failed:', err); return defaultConfig; // Fall back gracefully }costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[1] - flush · flush-network-errorwarningWhenNetwork failure during batch send, HTTP error response from PostHog API (non-2xx), or event payload too large (413 — triggers automatic batch size reduction and retry, but ultimately throws if still failing). After retry exhaustion, error propagates.Throws
PostHogFetchHttpError — on HTTP error responses from PostHog API. Properties: .response (Response), .name === 'PostHogFetchHttpError'. Note: 413 errors trigger batch size halving before finally propagating. PostHogFetchNetworkError — on network/fetch failures. Properties: .error (underlying Error), .name === 'PostHogFetchNetworkError'.Required handlingCaller SHOULD wrap in try-catch for serverless flush patterns. Flush failures mean analytics events are lost — not business-critical, but unhandled rejections crash serverless request handlers or interfere with graceful shutdown. Minimum handling: try { await posthog.flush(); } catch (err) { console.error('PostHog flush failed, events may be lost:', err); // Non-critical — continue normal execution }costlowin prodimmediate exceptionusers seedegraded performancevisibilitysilentSources[1] - getRemoteConfigPayload · get-remote-config-missing-personal-api-keyerrorWhenPostHog client is instantiated without a personalApiKey option (or with a project API key that starts with 'phc_' rather than a personal key starting with 'phx_'). getRemoteConfigPayload() throws synchronously before any network call.Throws
Error: 'Personal API key is required for remote config payload decryption' (synchronous throw — not a Promise rejection, but wrapping in try-catch handles both).Required handlingCaller MUST wrap in try-catch AND ensure personalApiKey is configured. Missing key configuration causes every getRemoteConfigPayload() call to throw, crashing the feature entirely on first use. Minimum handling: try { const config = await posthog.getRemoteConfigPayload('server-config'); return config as ServerConfig ?? defaultConfig; } catch (err) { if (err instanceof Error && err.message.includes('Personal API key')) { console.error('PostHog misconfiguration: personalApiKey required for remote config'); } else { console.error('Failed to fetch remote config:', err); } return defaultConfig; }costmediumin prodimmediate exceptionusers seedegraded performancevisibilitysilentSources[1] - getRemoteConfigPayload · get-remote-config-network-errorerrorWhenNetwork failure, DNS resolution failure, PostHog API unreachable, API key unauthorized (403), or server-side error during remote config fetch.Throws
PostHogFetchHttpError or PostHogFetchNetworkError (from @posthog/core). Same network error types as other PostHog async methods.Required handlingCaller MUST wrap in try-catch. Remote config fetch failures mean the application runs with stale or default configuration — critical if the config controls behavior that affects all users. Same try-catch block as missing-personal-api-key handles both error cases.costmediumin prodimmediate exceptionusers seedegraded performancevisibilitysilentSources[1]
Sources
Every postcondition cites at least one of these. Numbered to match the footnotes above.
Need a different package?
Request a profile