Skill 104 · Resolving Ingestion Warnings
Subchapter 104.11
references/fixing-invalid-heatmap-data.mdMarkdown4 KBView on GitHub
An event carried a malformed $heatmap_data payload. The event itself survived — only heatmap data was discarded, so this shows up as heatmaps missing clicks/pages rather than missing events.
Category , severity . Three variants tell you how much was lost:
eventwarning| Type | What was discarded |
|---|---|
invalid_heatmap_data | The entire $heatmap_data payload for that event — it didn’t parse at all |
rejecting_heatmap_data_with_invalid_url | One entry: its key (the page URL) was empty or not a string; other entries processed |
rejecting_heatmap_data_with_invalid_items | One entry: its URL mapped to something other than an array of items; other entries processed |
$heatmap_data is normally generated by posthog-js — hand-built payloads are almost always the source of these warnings. The expected shape:
{
$heatmap_data: {
'https://app.example.com/dashboard': [ // key: the page URL, a non-empty string
{ x: 120, y: 340, target_fixed: false, type: 'click' }, // value: an ARRAY of items
],
},
}The classic bugs: a null/undefined URL variable interpolated into the key, a single item object where the array should be, or a stringified/otherwise mangled structure from a custom proxy or mobile bridge.
posthog:execute-sql: SELECT timestamp, details FROM system.ingestion_warnings WHERE type IN ('invalid_heatmap_data', 'rejecting_heatmap_data_with_invalid_url', 'rejecting_heatmap_data_with_invalid_items') AND timestamp > now() - INTERVAL 7 DAY ORDER BY timestamp DESC LIMIT 20 (narrow to a single type to isolate one variant). In the details JSON, the rejecting_* entries include the offending heatmapUrl and session_id; invalid_heatmap_data entries carry the event_uuid of the event whose payload failed entirely (read via JSONExtractString(details, 'heatmapUrl') and friends). The rejecting_* types are debounced per session — each entry stands for a session’s worth of skips.$lib / $lib_version from the affected sessions’ events (posthog:execute-sql on the events table, filtered to the sampled session/distinct IDs) and compare against unaffected traffic. Warnings clustering on old posthog-js versions point to an outdated SDK producing a stale payload shape — the fix is an upgrade, not payload surgery. Mixed versions are common when some deploys/pages pin an old snippet.before_send hook, a custom transport). If $heatmap_data is built by hand (custom canvas apps, mobile), inspect that construction against the shape above.$heatmap_data — remove or fix any middleware/proxy that rewrites it.{x, y, target_fixed, type} items as values. Or better: capture the interactions as regular events and let PostHog’s own heatmap capture do the heatmap part.Re-run the affected pages/flows, re-query system.ingestion_warnings with posthog:execute-sql (filter type IN ('invalid_heatmap_data', 'rejecting_heatmap_data_with_invalid_url', 'rejecting_heatmap_data_with_invalid_items'), timestamp after your fix) — no new occurrences — and confirm the heatmap for the affected URLs starts accumulating data again.