Skip to content

fix: drop empty event names locally and count duplicates capture dropped - #989

Open
turnipdabeets wants to merge 5 commits into
feat/v4-capture-v1from
fix/v4-v1-rubric-followups
Open

turnipdabeets wants to merge 5 commits into
feat/v4-capture-v1from
fix/v4-v1-rubric-followups

Conversation

@turnipdabeets

@turnipdabeets turnipdabeets commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

Three small capture v1 parity gaps on the 4.0 branch, so iOS matches the other v1 SDKs:

  • Empty event names were sent and then dropped by capture. Typed v1 SDKs drop "" locally instead. capture("") and captureAi("") now drop the event before it's queued, including when beforeSend renames it to "". captureAi returns nil, and the first one is logged with debug on.

  • duplicates_dropped: capture v1 now keeps the first copy of a repeated UUID and reports how many later copies it dropped on that result. The SDK ignored the field. It now adds it to the per-batch debug count (Capture: 0 dropped, 2 duplicates dropped, 0 to retry.), and a repeated UUID in one request takes its first copy's result once instead of being counted per copy.

  • Typed values win key by key: userProperties, userPropertiesSetOnce and groups now win key by key over $set, $set_once and $groups maps passed in the same call's properties, matching posthog-android. Before, a $set or $set_once in the properties replaced the per-call value, and $groups in the properties won per key.

iOS generates a fresh v7 UUID for every event and callers can't set one, so duplicates only happen through an SDK bug. Known limit: if the kept copy comes back retry, the queue keeps every record with that UUID (records are matched by UUID), so a dropped copy is resent with it. Capture drops it again, and nothing is lost.

Stacked on #938.

💚 How did you test it?

  • New tests in PostHogCaptureV1Test: empty names via capture, captureAi, and a beforeSend rename are not sent; duplicates_dropped is counted once per UUID. Both fail on the base branch and pass here.
  • swift test --no-parallel --filter PostHogCaptureV1Test: 48 tests pass.
  • New PostHogSDKTests case for typed values winning key by key fails on the base branch and passes here; PostHogSDKTests (79 tests) passes.
  • swiftformat --lint and swiftlint show no new warnings.

📝 Checklist

  • All commits are signed (see Commit signing)
  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran pnpm changeset to generate a changeset file (added a line to the existing v4-capture-v1 changeset)

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • Claude Code compared the 4.0 capture v1 branch against the cross-SDK v1 requirements and fixed the two gaps it found here.
  • The empty-name check runs after beforeSend, so a hook can't rename an event to "" past it. Whitespace-only names are still sent, matching the other v1 SDKs, which only reject "".
  • duplicates_dropped goes into the existing debug count, since iOS has no per-event failure callback.

🤖 Generated with Claude Code

Drop an event whose name is empty after beforeSend before it's queued,
logging the first one. Read duplicates_dropped from capture v1 results and
add it to the per-batch debug count.
@turnipdabeets turnipdabeets self-assigned this Oct 28, 2026
@turnipdabeets
turnipdabeets requested a review from a team as a code owner October 28, 2026 23:25
Comment thread PostHog/PostHogApi.swift Outdated
@veria-ai

veria-ai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

posthog-ios Compliance Report

Date: 2026-10-29T19:52:06.591697+00:00
Duration: 967621ms

✅ All Tests Passed!

233/233 tests passed


Capture_V1 Tests

✅ 106/106 tests passed

View Details
Test Status Duration
Endpoint And Method.Targets V1 Endpoint ✅ 3158ms
Endpoint And Method.Does Not Use Legacy Endpoints ✅ 459ms
Required Headers.Has Authorization Bearer Header ✅ 2862ms
Required Headers.Has Content Type Json ✅ 313ms
Required Headers.Has Posthog Sdk Info Format ✅ 2824ms
Required Headers.Has Posthog Attempt Header ✅ 394ms
Required Headers.Has Posthog Request Id ✅ 2767ms
Required Headers.Has Posthog Request Timestamp ✅ 2784ms
Required Headers.Has User Agent ✅ 2829ms
Body Format.Body Has Created At And Batch ✅ 2822ms
Body Format.No Api Key In Body ✅ 2772ms
Body Format.No Sent At In Body ✅ 2752ms
Event Format.Event Has Required Root Fields ✅ 2831ms
Event Format.Event Uuid Is Valid ✅ 2825ms
Event Format.Event Timestamp Is Rfc3339 ✅ 2706ms
Event Format.Non Utc Event Timestamp Is Converted To Utc ✅ 2745ms
Event Format.Distinct Id Is String ✅ 408ms
Event Format.Distinct Id At Root Not Properties ✅ 2701ms
Event Format.Custom Properties Preserved ✅ 2780ms
Event Format.Set Properties Preserved ✅ 2851ms
Event Format.Set Once Properties Preserved ✅ 412ms
Event Format.Groups Properties Preserved ✅ 2762ms
Event Format.Sdk Generates Uuid If Not Provided ✅ 2832ms
Event Format.Event Has Required Root Fields Batch ✅ 2752ms
Event Format.Event Uuid Is Valid Batch ✅ 2891ms
Event Format.Event Timestamp Is Rfc3339 Batch ✅ 2880ms
Event Format.Distinct Id Is String Batch ✅ 2832ms
Event Format.Distinct Id At Root Not Properties Batch ✅ 2799ms
Event Format.Custom Properties Preserved Batch ✅ 2868ms
Event Format.Set Properties Preserved Batch ✅ 2877ms
Event Format.Set Once Properties Preserved Batch ✅ 368ms
Event Format.Groups Properties Preserved Batch ✅ 2753ms
Event Format.Sdk Generates Uuid If Not Provided Batch ✅ 2767ms
Batch Behavior.Multiple Events In Single Batch ✅ 2904ms
Batch Behavior.Batch Envelope Smoke ✅ 2696ms
Batch Behavior.Flush With No Events Sends Nothing ✅ 296ms
Batch Behavior.Flush At Triggers Batch ✅ 1199ms
Batch Behavior.Created At Reflects Batch Creation Time ✅ 419ms
Deduplication.Generates Unique Uuids ✅ 3120ms
Deduplication.Different Events Same Content Different Uuids ✅ 2931ms
Deduplication.Preserves Uuid On Retry ✅ 9174ms
Deduplication.Preserves Timestamp On Retry ✅ 9314ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 7927ms
Deduplication.No Duplicate Events In Batch ✅ 2802ms
Header Behavior On Retry.Attempt Header Starts At One ✅ 2737ms
Header Behavior On Retry.Attempt Header Increments On Retry ✅ 16271ms
Header Behavior On Retry.Request Id Preserved On Retry ✅ 9284ms
Header Behavior On Retry.Different Requests Have Different Request Ids ✅ 7345ms
Header Behavior On Retry.Request Timestamp Changes On Retry ✅ 9098ms
Response Format Validation.Success Response Has Uuid Keyed Results ✅ 2717ms
Response Format Validation.Success Response Has Ok For Each Event ✅ 2840ms
Response Format Validation.Success No Retry After When All Ok ✅ 2796ms
Response Format Validation.Success Retry After Present When Retry Events ✅ 2808ms
Response Format Validation.Success No Retry After When Drop Only ✅ 2864ms
Response Format Validation.Response Echoes Request Id ✅ 2802ms
Retry Behavior.Retries On 408 ✅ 9141ms
Retry Behavior.Retries On 500 ✅ 9164ms
Retry Behavior.Retries On 503 ✅ 11732ms
Retry Behavior.Retries On 504 ✅ 8912ms
Retry Behavior.Retries On 404 ✅ 9286ms
Retry Behavior.Retries On 429 ✅ 9280ms
Retry Behavior.Retries On 502 ✅ 5313ms
Retry Behavior.Retryable Errors Have Retry After ✅ 5797ms
Retry Behavior.Respects Retry After On Retryable Error ✅ 10771ms
Retry Behavior.Does Not Retry On 400 ✅ 2395ms
Retry Behavior.Does Not Retry On 401 ✅ 2252ms
Retry Behavior.Does Not Retry On 402 ✅ 4783ms
Retry Behavior.Does Not Retry On 413 ✅ 4723ms
Retry Behavior.Does Not Retry On 415 ✅ 4680ms
Retry Behavior.Non Retryable Errors Have No Retry After ✅ 4694ms
Retry Behavior.Implements Backoff ✅ 18851ms
Partial Batch Handling.Handles 200 Full Success ✅ 2414ms
Partial Batch Handling.Handles 200 With All Ok ✅ 5704ms
Partial Batch Handling.Does Not Retry Dropped Events ✅ 5766ms
Partial Batch Handling.Does Not Retry Warning Events ✅ 5739ms
Partial Batch Handling.Prunes Ok Events On Partial Retry ✅ 7894ms
Partial Batch Handling.Prunes Dropped Events On Partial Retry ✅ 8091ms
Partial Batch Handling.Retries Only Retry Events From Partial ✅ 7890ms
Partial Batch Handling.Partial Retry Preserves Uuids ✅ 7809ms
Partial Batch Handling.Partial Retry Attempt Header Increments ✅ 7761ms
Partial Batch Handling.Partial Retry Request Id Preserved ✅ 7807ms
Partial Batch Handling.Respects Retry After On Partial ✅ 8110ms
Partial Batch Handling.Unknown Result Treated As Terminal ✅ 5776ms
Partial Batch Handling.Mixed Ok Drop Warning No Retry ✅ 5795ms
Compression.Sends Gzip Content Encoding ✅ 2661ms
Compression.No Content Encoding When Disabled ✅ 2664ms
Compression.Compressed Body Is Decompressible ✅ 2747ms
Error Handling.Does Not Retry On Unknown 4Xx ✅ 4676ms
Event Options.Cookieless Mode Override ✅ 2626ms
Event Options.Disable Skew Correction Override ✅ 2857ms
Event Options.Process Person Profile Override ✅ 2800ms
Event Options.Product Tour Id Override ✅ 2802ms
Event Options.Unset Options Omitted ✅ 2735ms
Event Options.Options Override In Batch ✅ 2696ms
Event Options.Unknown Option Passes Through ✅ 2768ms
Event Options.Option Value Is Not Converted ✅ 2761ms
Event Options.Lenient Bool Option Values Are Not Converted ✅ 237ms
Event Options.Option Wins Over Legacy Property ✅ 2639ms
Event Options.Legacy Property Fills Unset Option ✅ 2764ms
Event Options.Null Option Falls Back To Legacy Property ✅ 2807ms
Property Layers.Caller Property Beats Registered Property ✅ 2727ms
Property Layers.Before Send Runs Last ✅ 2700ms
Fail Open.Empty Api Key Sends Nothing ✅ 1317ms
Fail Open.Personal Api Key Sends Nothing ✅ 1250ms
Geoip And Historical Migration.Geoip Disable Injected Into Properties ✅ 2775ms
Geoip And Historical Migration.Historical Migration Absent By Default ✅ 2688ms

Capture_Ai_V1 Tests

✅ 110/110 tests passed

View Details
Test Status Duration
Endpoint And Method.Targets V1 Endpoint ✅ 2717ms
Endpoint And Method.Does Not Use Legacy Endpoints ✅ 2679ms
Endpoint And Method.Capture Does Not Reroute Ai Named Events ✅ 2760ms
Endpoint And Method.Capture Ai Does Not Reroute Non Ai Events ✅ 2643ms
Lane Isolation.Failing Ai Lane Does Not Delay Analytics ✅ 11711ms
Required Headers.Has Authorization Bearer Header ✅ 2721ms
Required Headers.Has Content Type Json ✅ 2645ms
Required Headers.Has Posthog Sdk Info Format ✅ 2717ms
Required Headers.Has Posthog Attempt Header ✅ 384ms
Required Headers.Has Posthog Request Id ✅ 2781ms
Required Headers.Has Posthog Request Timestamp ✅ 506ms
Required Headers.Has User Agent ✅ 3056ms
Body Format.Body Has Created At And Batch ✅ 3009ms
Body Format.No Api Key In Body ✅ 2896ms
Body Format.No Sent At In Body ✅ 349ms
Event Format.Event Has Required Root Fields ✅ 2852ms
Event Format.Event Uuid Is Valid ✅ 2887ms
Event Format.Event Timestamp Is Rfc3339 ✅ 721ms
Event Format.Non Utc Event Timestamp Is Converted To Utc ✅ 2929ms
Event Format.Distinct Id Is String ✅ 462ms
Event Format.Distinct Id At Root Not Properties ✅ 3088ms
Event Format.Custom Properties Preserved ✅ 3245ms
Event Format.Set Properties Preserved ✅ 2754ms
Event Format.Set Once Properties Preserved ✅ 3013ms
Event Format.Groups Properties Preserved ✅ 2811ms
Event Format.Sdk Generates Uuid If Not Provided ✅ 2749ms
Event Format.Event Has Required Root Fields Batch ✅ 2838ms
Event Format.Event Uuid Is Valid Batch ✅ 2737ms
Event Format.Event Timestamp Is Rfc3339 Batch ✅ 563ms
Event Format.Distinct Id Is String Batch ✅ 2825ms
Event Format.Distinct Id At Root Not Properties Batch ✅ 2876ms
Event Format.Custom Properties Preserved Batch ✅ 3045ms
Event Format.Set Properties Preserved Batch ✅ 2866ms
Event Format.Set Once Properties Preserved Batch ✅ 3127ms
Event Format.Groups Properties Preserved Batch ✅ 583ms
Event Format.Sdk Generates Uuid If Not Provided Batch ✅ 2916ms
Batch Behavior.Multiple Events In Single Batch ✅ 2902ms
Batch Behavior.Batch Envelope Smoke ✅ 2848ms
Batch Behavior.Flush At Triggers Batch ✅ 1237ms
Batch Behavior.Created At Reflects Batch Creation Time ✅ 2764ms
Deduplication.Generates Unique Uuids ✅ 2934ms
Deduplication.Different Events Same Content Different Uuids ✅ 3327ms
Deduplication.Preserves Uuid On Retry ✅ 9177ms
Deduplication.Preserves Timestamp On Retry ✅ 8818ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 7691ms
Deduplication.No Duplicate Events In Batch ✅ 2732ms
Header Behavior On Retry.Attempt Header Starts At One ✅ 333ms
Header Behavior On Retry.Attempt Header Increments On Retry ✅ 10392ms
Header Behavior On Retry.Request Id Preserved On Retry ✅ 5401ms
Header Behavior On Retry.Different Requests Have Different Request Ids ✅ 4922ms
Header Behavior On Retry.Request Timestamp Changes On Retry ✅ 5342ms
Response Format Validation.Success Response Has Uuid Keyed Results ✅ 2641ms
Response Format Validation.Success Response Has Ok For Each Event ✅ 2800ms
Response Format Validation.Success No Retry After When All Ok ✅ 2733ms
Response Format Validation.Success Retry After Present When Retry Events ✅ 2877ms
Response Format Validation.Success No Retry After When Drop Only ✅ 2751ms
Response Format Validation.Response Echoes Request Id ✅ 2664ms
Retry Behavior.Retries On 408 ✅ 8899ms
Retry Behavior.Retries On 500 ✅ 9150ms
Retry Behavior.Retries On 503 ✅ 11240ms
Retry Behavior.Retries On 504 ✅ 9323ms
Retry Behavior.Retries On 404 ✅ 8737ms
Retry Behavior.Retries On 429 ✅ 8804ms
Retry Behavior.Retries On 502 ✅ 10164ms
Retry Behavior.Retryable Errors Have Retry After ✅ 6384ms
Retry Behavior.Respects Retry After On Retryable Error ✅ 10727ms
Retry Behavior.Does Not Retry On 400 ✅ 4753ms
Retry Behavior.Does Not Retry On 401 ✅ 4900ms
Retry Behavior.Does Not Retry On 402 ✅ 4883ms
Retry Behavior.Does Not Retry On 413 ✅ 4773ms
Retry Behavior.Does Not Retry On 415 ✅ 4838ms
Retry Behavior.Non Retryable Errors Have No Retry After ✅ 2398ms
Retry Behavior.Implements Backoff ✅ 17851ms
Partial Batch Handling.Handles 200 Full Success ✅ 4746ms
Partial Batch Handling.Handles 200 With All Ok ✅ 5772ms
Partial Batch Handling.Does Not Retry Dropped Events ✅ 5835ms
Partial Batch Handling.Does Not Retry Warning Events ✅ 5790ms
Partial Batch Handling.Prunes Ok Events On Partial Retry ✅ 7875ms
Partial Batch Handling.Prunes Dropped Events On Partial Retry ✅ 7748ms
Partial Batch Handling.Retries Only Retry Events From Partial ✅ 7886ms
Partial Batch Handling.Partial Retry Preserves Uuids ✅ 7835ms
Partial Batch Handling.Partial Retry Attempt Header Increments ✅ 7929ms
Partial Batch Handling.Partial Retry Request Id Preserved ✅ 7869ms
Partial Batch Handling.Respects Retry After On Partial ✅ 7867ms
Partial Batch Handling.Unknown Result Treated As Terminal ✅ 5961ms
Partial Batch Handling.Mixed Ok Drop Warning No Retry ✅ 5954ms
Compression.Sends Gzip Content Encoding ✅ 2707ms
Compression.No Content Encoding When Disabled ✅ 2809ms
Compression.Compressed Body Is Decompressible ✅ 3034ms
Error Handling.Does Not Retry On Unknown 4Xx ✅ 4806ms
Event Options.Cookieless Mode Override ✅ 2824ms
Event Options.Disable Skew Correction Override ✅ 2744ms
Event Options.Process Person Profile Override ✅ 2815ms
Event Options.Product Tour Id Override ✅ 2694ms
Event Options.Unset Options Omitted ✅ 2806ms
Event Options.Options Override In Batch ✅ 2838ms
Event Options.Unknown Option Passes Through ✅ 2862ms
Event Options.Option Value Is Not Converted ✅ 427ms
Event Options.Lenient Bool Option Values Are Not Converted ✅ 432ms
Event Options.Option Wins Over Legacy Property ✅ 2748ms
Event Options.Legacy Property Fills Unset Option ✅ 2820ms
Event Options.Null Option Falls Back To Legacy Property ✅ 2739ms
Event Size.Event Over Ceiling Is Not Sent ✅ 8149ms
Event Size.Batches Close Before Byte Target ✅ 8411ms
Geoip And Historical Migration.Geoip Disable Injected Into Properties ✅ 2824ms
Geoip And Historical Migration.Historical Migration Absent By Default ✅ 2903ms
Property Layers.Caller Property Beats Registered Property ✅ 2882ms
Property Layers.Before Send Runs Last ✅ 547ms
Fail Open.Empty Api Key Sends Nothing ✅ 1383ms
Fail Open.Personal Api Key Sends Nothing ✅ 1222ms

Feature_Flags Tests

✅ 17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 2870ms
Request Payload.Flags Request Uses V2 Query Param ✅ 2720ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 2829ms
Request Payload.Flags Request Omits Authorization Header ✅ 3026ms
Request Payload.Token In Flags Body Matches Init ✅ 2735ms
Request Payload.Groups Round Trip ✅ 2831ms
Request Payload.Groups Default To Empty Object ✅ 2719ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 2828ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 2720ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 2867ms
Request Lifecycle.No Flags Request On Init Alone ✅ 83ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 2816ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 5568ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 2837ms
Retry Behavior.Retries Flags On 502 ✅ 2793ms
Retry Behavior.Retries Flags On 504 ✅ 2877ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 3000ms

turnipdabeets and others added 2 commits October 29, 2026 11:49
… person processing as an option (#990)

Count a capture v1 2xx whose body can't be read as per-event results as a
dropped batch in the debug log, not as delivered. The records are still
removed and not retried.

Save person processing in the crash context as the process_person_profile
option instead of the legacy $process_person_profile property, and pass it
when the crash and memory exception reporters send the $exception.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…log terminal batch losses

- Check for an empty event name in queueEvent, so screen, identify,
  alias, group and autocapture events that beforeSend renames to ""
  are dropped like capture's.
- identify ignores a whitespace-only distinct ID and no longer logs it.
- The capture v1 aggregate debug line names the endpoint and status,
  and counts the batch on a terminal status (400, 401, 402, 415, or a
  single-event 413).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant