Skip to content

fix(flags): relax StatsigEventSerializer to accept nested user fields and blank values#119478

Draft
billyvg wants to merge 1 commit into
masterfrom
claude/serene-cannon-fu0scw
Draft

fix(flags): relax StatsigEventSerializer to accept nested user fields and blank values#119478
billyvg wants to merge 1 commit into
masterfrom
claude/serene-cannon-fu0scw

Conversation

@billyvg

@billyvg billyvg commented Jul 12, 2026

Copy link
Copy Markdown
Member

Summary

Fixes a production DeserializationError in the Statsig webhook handler (SENTRY-5J6Z) that has been generating ~2.75M errors affecting 4,825 users since 2026-02-07.

Root Cause

StatsigEventSerializer was too strict about the shape of optional fields that are never actually used in the audit log business logic:

  1. user DictField with child=serializers.CharField(): Statsig sends nested objects (e.g. customIDs: {"companyID": "acme"}, custom: {"role": "admin"}, statsigEnvironment: {"tier": "production"}) as values within the user dict. The child=serializers.CharField() constraint rejects any value that isn't a plain string.

  2. value = serializers.CharField(required=False): Statsig sends value="" (empty string) which fails the default allow_blank=False on CharField.

  3. timeUUID = serializers.UUIDField(required=False): Statsig sends arbitrary string values that aren't valid UUIDs.

Fix

  • Remove child=serializers.CharField() from user DictField — the field accepts any dict structure now
  • Add allow_blank=True to value CharField
  • Change timeUUID from UUIDField to CharField(allow_blank=True) — we never validate or use this field

None of these fields are used in the audit log rows produced by StatsigProvider.handle(). The business logic only reads user.email, user.userID, user.name, userID, eventName, timestamp, and metadata from each event.

Evidence

Sentry issue: https://sentry.sentry.io/issues/SENTRY-5J6Z

  • 2,753,422 events, 4,825 users affected
  • Seer root cause: "Overly strict validation in StatsigEventSerializer for unused fields causes DeserializationError when processing Statsig webhooks containing unexpected formats like nested objects or empty strings."

Example failing payload fragment:

{
  "user": {
    "userID": "...",
    "customIDs": {"companyID": "acme"},
    "custom": {"role": "admin"},
    "statsigEnvironment": {"tier": "production"}
  },
  "value": "",
  "timeUUID": "not-a-uuid"
}

Test Plan

Added test_handle_nested_user_fields which covers the exact pattern from the failing production events (nested user dict values, blank value, non-UUID timeUUID).

Session: https://claude.ai/code/session_0184E4PYCz4ekKHjv3cZkUJT


Generated by Claude Code

Statsig sends nested objects (customIDs, custom, statsigEnvironment) as
values within the `user` field, empty strings for `value`, and non-UUID
strings for `timeUUID`. The overly-strict serializer was raising
DeserializationError for all of these, even though none of these fields
are used in the audit log logic.

Fixes:
- Remove child=serializers.CharField() from user DictField so nested
  objects/lists are accepted
- Add allow_blank=True to value CharField
- Change timeUUID from UUIDField to CharField since it's not validated
  and callers send arbitrary strings

Fixes SENTRY-5J6Z

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0184E4PYCz4ekKHjv3cZkUJT
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants