Diagnosing Missing Recordings
PostHogOfficialPublished by PostHog
- Skill
- Workflow
- Data and AI
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured diagnostic process for investigating why session recordings are missing or failing to capture. By leveraging PostHog's SDK diagnostic signals—such as recording status, trigger states, and buffer metrics—the agent can identify whether a recording was blocked by ad blockers, disabled in settings, sampled out, or failed due to ingestion issues. The skill guides the user through querying event properties to determine the root cause and provides actionable recommendations for resolution.
What it does
- Verifies if a specific session recording exists in the project.
- Queries SDK diagnostic signals from event properties to determine recording state.
- Interprets complex signal combinations to identify specific failure modes like ad blocking or sampling.
- Analyzes project-wide patterns to distinguish between individual user issues and configuration errors.
- Provides targeted troubleshooting recommendations based on the identified verdict.
- Maps diagnostic signals to clear, actionable explanations for missing data.
When to use it
- When a user reports that a specific session has no replay available.
- When investigating why recordings are not appearing for a project or specific user segment.
- When troubleshooting potential configuration issues like sample rates or recording triggers.
- When needing to distinguish between technical capture failures and expected behavior like sampling.
Advantages
- Reduces manual investigation time by providing a clear, priority-ordered diagnostic logic.
- Uses standardized SDK signals to ensure consistent and accurate troubleshooting.
- Helps differentiate between client-side issues (ad blockers) and server-side configuration (project settings).
- Provides clear, actionable steps for both the agent and the end-user.
Usage examples
- “Why is there no recording for session ID 12345?”
- The agent checks the recording existence, queries the diagnostic signals for that session, and reports if it was sampled out, blocked by an ad blocker, or if the trigger never fired.
- “Our project has very few recordings, can you help?”
- The agent performs a project-wide analysis of recent sessions to identify if the issue is a global setting, an aggressive sample rate, or a widespread script-loading failure.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/posthog/diagnosing-missing-recordings/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip diagnosing-missing-recordings.zip -d .agents/skills
Documentation by PostHog
Diagnosing missing session recordings
When a user asks "why wasn't this session recorded?" or "why don't I have any recordings?", follow this workflow to systematically diagnose the cause.
Available tools
| Tool | Purpose |
|---|---|
posthog:execute-sql | Query session event properties for diagnostic signals |
posthog:session-recording-get | Check if a recording actually exists for the session |
posthog:query-session-recordings-list | Search for recordings matching criteria |
Diagnostic signals
The PostHog SDK emits diagnostic properties on every event that explain the recording state. See the diagnostic signals reference for the full list.
The key signals are:
$has_recording— whether PostHog has a stored recording for this session$recording_status— SDK state:active,buffering,disabled,sampled,paused$session_recording_start_reason— why recording started or didn't$sdk_debug_recording_script_not_loaded— recorder script blocked (ad blocker)$sdk_debug_replay_*_trigger_status— trigger states (URL, event, linked flag)$replay_sample_rate— configured sample rate at capture time
Workflow
Step 1 — Check if the recording exists
If the user provides a session ID, first check whether a recording actually exists:
posthog:session-recording-get
{
"id": "<session_id>"
}If it returns found: false with reason recording_not_found, no recording exists for this session in the active project.
Proceed to Step 2 to diagnose why.
If it returns recording metadata, the recording exists — the issue is likely UI/filtering, not capture.
Any other error (for example, a missing project or a permission error) is not evidence about the recording.
Fix that error first.
Step 2 — Query diagnostic signals from events
Query the most recent event for the session to get SDK diagnostic properties:
posthog:execute-sql
SELECT
properties.$has_recording AS has_recording,
properties.$recording_status AS recording_status,
properties.$session_recording_start_reason AS start_reason,
properties.$sdk_debug_recording_script_not_loaded AS script_not_loaded,
properties.$sdk_debug_replay_url_trigger_status AS url_trigger,
properties.$sdk_debug_replay_event_trigger_status AS event_trigger,
properties.$sdk_debug_replay_linked_flag_trigger_status AS flag_trigger,
properties.$replay_sample_rate AS sample_rate,
properties.$sdk_debug_replay_internal_buffer_length AS buffer_length,
properties.$sdk_debug_replay_flushed_size AS flushed_size,
properties.$lib AS sdk_library,
properties.$lib_version AS sdk_version
FROM events
WHERE $session_id = '<session_id>'
ORDER BY timestamp DESC
LIMIT 1Step 3 — Diagnose the verdict
Use the diagnosis logic reference to interpret the signals. The verdicts in priority order:
Recording exists (
$has_recording = true) — recording is captured, issue is elsewhereAd blocked (script) (
$sdk_debug_recording_script_not_loaded = true) — browser extension blocking the recorder script from loadingDisabled (
$recording_status = 'disabled') — replay turned off in settings or SDK configTrigger pending (trigger statuses are
trigger_pending, none matched) — recording gated on trigger that never firedSampled out (
$session_recording_start_reason = 'sampled_out') — excluded by sample rateBuffering empty (
$recording_status = 'buffering', buffer length = 0, nothing flushed) — initialized but no snapshots producedFlush blocked (buffer length climbs across events while
flushed_sizestays at 0) — snapshots are produced but the/s/ingestion endpoint is blocked by an ad blocker or misconfigured reverse proxy. Detecting this requires querying the trend across the session's events — see example 3 in examples.mdUnknown — signals don't match a known pattern
Step 4 — Check project-level settings (if no session ID)
When the user asks about recordings missing project-wide (no specific session), query for recent sessions to check the pattern:
posthog:execute-sql
SELECT
$session_id,
properties.$recording_status AS recording_status,
properties.$session_recording_start_reason AS start_reason,
properties.$sdk_debug_recording_script_not_loaded AS script_not_loaded,
properties.$replay_sample_rate AS sample_rate
FROM events
WHERE event = '$pageview'
AND timestamp > now() - INTERVAL 1 DAY
GROUP BY
$session_id,
recording_status,
start_reason,
script_not_loaded,
sample_rate
ORDER BY max(timestamp) DESC
LIMIT 10Look for patterns:
All
disabled→ replay is turned off in project settingsAll
sampled_outwith low sample rate → sample rate too aggressiveAll
script_not_loaded→ likely a CSP or deployment issue, not just one user's ad blockerMix of statuses → per-session issue, dig into specifics
Step 5 — Provide actionable recommendations
Based on the verdict, recommend specific actions:
| Verdict | Recommendation |
|---|---|
| Ad blocked | User's browser extension is blocking rrweb. Suggest trying without ad blocker, or using a proxy/custom domain for the recorder script |
| Disabled | Check project replay settings — recording may be turned off. Link to Settings > Session replay |
| Trigger pending | The configured trigger (URL pattern, event, or feature flag) never matched. Review trigger configuration |
| Sampled out | Increase the sample rate in project settings, or use a trigger to guarantee capture for important sessions |
| Buffering empty | Page closed before first snapshot. Common with very short sessions or single-page navigations. Consider lowering minimum duration |
| Unknown | Direct user to troubleshooting docs: https://posthog.com/docs/session-replay/troubleshooting |
Examples
See real-world diagnostic examples showing how signal combinations map to verdicts. Use these to calibrate your interpretation of query results.
Tips
If
$lib_versionis very old, some diagnostic signals won't be present. Note this to the user — upgrading the SDK will provide better diagnostics.A session might have events but no recording if the recording was deleted due to retention. Check the session's timestamp against the project's retention period.
If
$has_recordingis true but the user can't find it, check if it's filtered out by duration, activity threshold, or playlist filters.
Related skills
diagnosing-sdk-health— an outdated SDK is a common root cause and blunts the diagnostic signalsfinding-sessions-to-watch— once capture works, pick the sessions worth watchinginvestigating-replay— analyze the recording once it exists
Files
- ATTRIBUTION.mdAdded by AutoGineer877 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md7.8 KiB
- references/diagnosis-logic.md4.4 KiB
- references/diagnostic-signals.md5.9 KiB
- references/examples.md4.4 KiB
references/diagnosis-logic.md
Diagnosis logic
This describes the priority-ordered logic for interpreting diagnostic signals. Evaluate conditions top-to-bottom - the first match is the verdict.
Contents
Decision tree
Verdict descriptions
Decision tree
$has_recording == true?
→ CAPTURED: recording exists, issue is elsewhere (UI filtering, still processing)
$sdk_debug_recording_script_not_loaded == true?
→ AD_BLOCKED: recorder script failed to load (ad blocker, CSP, network error)
$recording_status == 'disabled'?
→ DISABLED: replay turned off in project settings or SDK config
Any trigger status is 'trigger_pending' AND none is 'trigger_matched'?
→ TRIGGER_PENDING: recording gated on trigger that never fired
$session_recording_start_reason == 'sampled_out'?
→ SAMPLED_OUT: excluded by configured sample rate
$recording_status == 'buffering' AND buffer_length == 0 AND flushed_size == 0 (or null)?
→ BUFFERING_EMPTY: SDK initialized but produced no snapshots
$recording_status == 'sampled' OR ($recording_status == 'active' AND flushed_size > 0)?
→ CAPTURED: SDK was actively recording and flushed data (recording should exist, may be processing or deleted by retention)
$recording_status == 'paused'?
→ PAUSED: recording is temporarily paused for this session
Buffer length climbs across the session's events AND flushed_size stays at 0?
→ FLUSH_BLOCKED: snapshots produced but ingestion endpoint blocked
(requires querying the trend across events, not a single row)
None of the above?
→ UNKNOWN: signals don't match a known patternVerdict descriptions
CAPTURED
The recording exists or was captured. If the user still can't find it:
It may still be processing (especially if recent)
It may be filtered out by duration, activity threshold, or playlist filters
It may have been deleted due to retention policy
AD_BLOCKED
The rrweb recorder script was blocked from loading. This is the most common cause of missing recordings for individual users. Typical causes:
Browser ad blocker extensions (uBlock Origin, AdBlock Plus, etc.)
Corporate content security policies (CSP)
Network-level blocking (Pi-hole, corporate proxies)
DISABLED
Recording is explicitly turned off. Check:
Project settings (Settings > Session replay)
SDK initialization config (
session_recording: { enabled: false })Runtime SDK calls (
posthog.set_config({ disable_session_recording: true }))
TRIGGER_PENDING
Recording was configured to only start when a trigger fires (URL pattern match, specific event, or feature flag). The trigger never matched during this session, so no recording was produced. Review the trigger configuration to ensure it covers the expected pages/events.
SAMPLED_OUT
The SDK randomly excluded this session based on the configured sample rate. This is expected behavior — if the sample rate is 50%, roughly half of sessions won't be recorded. To capture more sessions, increase the sample rate or use triggers for important flows.
BUFFERING_EMPTY
The SDK initialized in buffering mode but never produced snapshots. Common causes:
Very short session (page closed before first snapshot)
Minimum duration threshold not met
Page navigated away before buffer was flushed
PAUSED
Recording is temporarily paused for this session. This can happen when:
The SDK's
pause()method was called programmaticallyA consent mechanism paused recording pending user opt-in
The session exceeded a configured maximum duration
FLUSH_BLOCKED
The SDK is producing snapshots but they're not reaching PostHog.
Distinct from AD_BLOCKED (which is the script itself failing to load) —
here the script loaded and is working, but the POST /s/ upload is being blocked.
Detecting this requires looking at the trend of buffer/flush signals across multiple
events in the session (see example 3 in examples.md).
Typical causes:
Ad blocker blocking the ingestion endpoint (different from blocking the script)
Reverse proxy not forwarding
/s/correctly on self-hosted setupsCustom domain mismatch between recorder script and capture endpoint
UNKNOWN
The available signals don't match any known failure pattern. This can happen when:
SDK version is too old to emit diagnostic signals
Event properties were stripped or modified
An unusual SDK configuration is in use
Direct the user to the troubleshooting docs for manual investigation.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- PostHog
- Publisher
- PostHog (Official)
- Source
- PostHog/skills · skills/omnibus/diagnosing-missing-recordings · commit a5d30e7
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by PostHog.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download diagnosing-missing-recordings.zip