From posthog-pack
Diagnoses and fixes common PostHog SDK errors: missing events, undefined feature flags, 401/429 auth failures, init issues, and identity problems.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin posthog-packThis skill is limited to using the following tools:
Diagnosis and solutions for the most common PostHog integration errors. Covers event capture failures, authentication issues, feature flag problems, identity fragmentation, and SDK initialization errors.
Triages PostHog outages and integration issues with bash health checks (curl/kubectl), error handling for 401/429/500, graceful degradation, evidence collection, and postmortem steps.
Implements PostHog analytics for event tracking, user identification, feature flags, and dashboards in Next.js and React apps. Use when adding product analytics.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Share bugs, ideas, or general feedback.
Diagnosis and solutions for the most common PostHog integration errors. Covers event capture failures, authentication issues, feature flag problems, identity fragmentation, and SDK initialization errors.
posthog-js or posthog-node)phc_...) availableSymptoms: posthog.capture() calls execute without error but events never show in PostHog Activity tab.
Diagnoses and fixes:
// Problem A: Not flushing in serverless/scripts
// posthog-node queues events — they're lost if process exits before flush
const posthog = new PostHog('phc_...');
posthog.capture({ distinctId: 'user-1', event: 'test' });
// FIX: Always flush before exit
await posthog.shutdown(); // or await posthog.flush()
// Problem B: Wrong API host
posthog.init('phc_...', {
api_host: 'https://app.posthog.com', // WRONG — this is the UI
});
// FIX: Use the ingest endpoint
posthog.init('phc_...', {
api_host: 'https://us.i.posthog.com', // CORRECT for US Cloud
// api_host: 'https://eu.i.posthog.com', // For EU Cloud
});
// Problem C: Ad blocker blocking posthog-js requests
// FIX: Set up a reverse proxy (see posthog-sdk-patterns)
// next.config.js rewrites: /ingest/* → us.i.posthog.com/*
// Then: posthog.init('phc_...', { api_host: '/ingest' });
undefined or Wrong Value// Problem: Checking flag before flags are loaded
const value = posthog.isFeatureEnabled('my-flag'); // undefined — flags not ready
// FIX: Wait for flags to load
posthog.onFeatureFlags(() => {
const value = posthog.isFeatureEnabled('my-flag'); // Now has correct value
});
// Problem (server): No personalApiKey — falls back to remote evaluation
const ph = new PostHog('phc_...'); // Missing personalApiKey
const flag = await ph.getFeatureFlag('my-flag', 'user-1'); // Slow, may fail
// FIX: Add personalApiKey for local evaluation
const ph = new PostHog('phc_...', {
personalApiKey: process.env.POSTHOG_PERSONAL_API_KEY, // phx_...
});
// Now evaluates locally — faster and more reliable
set -euo pipefail
# Symptom: 401 when calling admin endpoints
curl "https://app.posthog.com/api/projects/$POSTHOG_PROJECT_ID/feature_flags/" \
-H "Authorization: Bearer phc_wrong_key_type"
# Returns: {"detail": "Authentication credentials were not provided."}
# FIX: Use Personal API Key (phx_...) for admin endpoints, not project key (phc_...)
curl "https://app.posthog.com/api/projects/$POSTHOG_PROJECT_ID/feature_flags/" \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" # Must be phx_...
# Check which key type you have:
echo "$POSTHOG_PERSONAL_API_KEY" | head -c 4
# phx_ = Personal API Key (correct for admin API)
# phc_ = Project API Key (only for event capture)
// Symptom: HTTP 429 on analytics endpoints
// PostHog rate limits: 240 req/min and 1200 req/hour for analytics endpoints
// Feature flag local eval: 600 req/min
// FIX: Implement backoff with Retry-After header
async function postHogRequest(url: string, options: RequestInit) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
console.warn(`PostHog rate limited. Retrying in ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
return postHogRequest(url, options); // Retry
}
return response;
}
// FIX: Cache insight results instead of polling
let cachedInsights: any = null;
let cacheExpiry = 0;
async function getInsights() {
if (cachedInsights && Date.now() < cacheExpiry) return cachedInsights;
const res = await postHogRequest(
`https://app.posthog.com/api/projects/${PROJECT_ID}/insights/trend/`,
{ headers: { Authorization: `Bearer ${PERSONAL_KEY}` } }
);
cachedInsights = await res.json();
cacheExpiry = Date.now() + 300000; // Cache 5 minutes
return cachedInsights;
}
// Problem: Same user appears as multiple persons in PostHog
// Cause: Different distinct_id on frontend vs backend
// Frontend captures with anonymous ID: "anon-abc123"
posthog.capture('page_viewed'); // distinct_id = "anon-abc123"
// Backend captures with user ID: "user-456"
serverPosthog.capture({
distinctId: 'user-456', // Different from frontend!
event: 'api_called',
});
// FIX: Call identify on frontend to merge anonymous → known
posthog.identify('user-456'); // Merges anon-abc123 → user-456
// FIX: Use same distinct_id on server as frontend
serverPosthog.capture({
distinctId: 'user-456', // Same as what frontend uses after identify
event: 'api_called',
});
// Problem: posthog.capture is not a function
// Cause: SDK not initialized or called server-side
// FIX: Guard all browser calls
if (typeof window !== 'undefined') {
posthog.init('phc_...', { api_host: 'https://us.i.posthog.com' });
}
// FIX: Check if initialized before capturing
if (typeof posthog.capture === 'function') {
posthog.capture('my_event');
}
// Problem: CSP (Content Security Policy) blocking PostHog
// FIX: Add PostHog domains to your CSP header
// connect-src: https://us.i.posthog.com https://us-assets.i.posthog.com
set -euo pipefail
# 1. Check PostHog API reachability
curl -s -o /dev/null -w "HTTP %{http_code}\n" https://us.i.posthog.com/healthz
# 2. Verify project API key works (send test event)
curl -s -X POST 'https://us.i.posthog.com/capture/' \
-H 'Content-Type: application/json' \
-d "{\"api_key\":\"$NEXT_PUBLIC_POSTHOG_KEY\",\"event\":\"diagnostic_test\",\"distinct_id\":\"debug\"}" | jq .
# 3. Verify personal API key works
curl -s "https://app.posthog.com/api/projects/" \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" | jq '.[0].name'
# 4. Check installed SDK versions
npm list posthog-js posthog-node 2>/dev/null || echo "No PostHog SDK found"
# 5. Check environment variables
env | grep -i posthog | sed 's/=.*/=***/'
| Error | HTTP Code | Cause | Solution |
|---|---|---|---|
| Events missing | N/A | Not flushed | await posthog.shutdown() |
| Auth failed | 401 | Wrong key type | Use phx_ for admin, phc_ for capture |
| Rate limited | 429 | Too many API calls | Backoff, cache results |
| Flag undefined | N/A | Flags not loaded | Use onFeatureFlags callback |
| CSP blocked | N/A | Missing CSP entry | Add us.i.posthog.com to connect-src |
| Duplicate users | N/A | Identity mismatch | Call posthog.identify() consistently |
For comprehensive debugging, see posthog-debug-bundle.