Skip to content

fix(data-warehouse): fail early on colliding Google Sheets headers - #71982

Merged
Gilbert09 merged 2 commits into
masterfrom
posthog-code/gsheets-normalized-header-collision
Jul 17, 2026
Merged

fix(data-warehouse): fail early on colliding Google Sheets headers#71982
Gilbert09 merged 2 commits into
masterfrom
posthog-code/gsheets-normalized-header-collision

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

When onboarding a Google Sheets data-warehouse source, sheet headers become table
column names via NamingConvention, which is case- and punctuation-insensitive. So
two headers that look distinct in the sheet — for example Task ID and task_id
collapse to the same column name.

gspread only rejects exact duplicate headers, so these near-duplicates pass its
check and the failure surfaces much later as an opaque duplicate column name error
deep in table creation, after the sync has already started. Because the failure is
deterministic, retries never recover, and the source silently stays in an error
state. Replay Vision surfaced a real onboarding session where a user got stuck in a
troubleshooting loop on exactly this: distinct-looking headers that collided after
normalization, with no message pointing at the cause.

Changes

  • Detect the collision when the header row is first read (google_sheets.py) and
    raise an actionable message naming the two offending headers and the column name
    they collapse to. Exact duplicates are still left to gspread's own check.
  • Map the new message as non-retryable in get_non_retryable_errors so this
    deterministic failure isn't retried forever.

No change to sync behavior for sheets with unique headers, and no change to which
sheets succeed — a sheet that would have failed cryptically now fails immediately
with a clear message.

How did you test this code?

Added parameterized unit tests in test_google_sheets.py (pure-function, no DB):

  • test_assert_unique_normalized_column_names_raises_on_normalized_collision
    covers case-only, punctuation-only, and mixed collisions, and asserts the raised
    message matches a non-retryable key (guards against a revert of the detection or
    a message that no longer matches a key, which would retry the failure forever).
  • test_assert_unique_normalized_column_names_allows_valid_headers — distinct
    headers, blank cells, and exact duplicates (left to gspread) do not raise.

Ran the affected tests locally with pytest (9 passed) and ruff check / ruff format --check on the touched files (clean). No manual end-to-end sync run.

🤖 Agent context

Autonomy: Fully autonomous

Authored by Claude Code (PostHog Code) while reviewing data-warehouse new-source
onboarding sessions for recurring friction. Invoked the /writing-tests skill
before adding the regression tests to run them through the value gate. Chose an
early, clear hard-error over silently auto-renaming colliding columns because it
matches the Google Sheets source's existing behavior (it already rejects duplicate
headers and asks the user to fix them) and avoids changing what data lands.


Created with PostHog Code

Sheet headers become table column names via NamingConvention, which is case- and
punctuation-insensitive. gspread only rejects exact duplicate headers, so headers
that differ only by case or punctuation (e.g. "Task ID" vs "task_id") slip through
and fail much later with an opaque "duplicate column name" error deep in table
creation, after the sync has already started.

Detect the collision when the header row is first read and raise an actionable
message naming the offending headers, and map it as non-retryable so the
deterministic failure isn't retried forever.

Generated-By: PostHog Code
Task-Id: cade8cc7-2ba9-4e90-8fb2-f130f0b98aaf
@github-actions

Copy link
Copy Markdown
Contributor

Hey @Gilbert09! 👋

It looks like your git author email on this PR isn't your @posthog.com address (owerstom@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 17, 2026 14:01
@trunk-io

trunk-io Bot commented Jul 17, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

…lision

Generated-By: PostHog Code
Task-Id: cade8cc7-2ba9-4e90-8fb2-f130f0b98aaf
@Gilbert09 Gilbert09 added the stamphog Request AI approval (no full review) label Jul 17, 2026

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small, contained fix to Google Sheets warehouse-source header validation with parameterized regression tests, authored by a member of the owning team; no data model, API contract, or ingestion-pipeline risk.

  • Author wrote 0% of the modified lines and has 8 merged PRs in these paths (familiarity MODERATE).
  • 👍 on the PR from hex-security-app[bot].
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 30L, 2F substantive, 64L/3F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1b-small (64L, 3F, single-area, fix)
stamphog 2.0.0b3 .stamphog/policy.yml @ d080cf4 · reviewed head 0ef0a78

@Gilbert09
Gilbert09 merged commit 1e7b356 into master Jul 17, 2026
256 checks passed
@Gilbert09
Gilbert09 deleted the posthog-code/gsheets-normalized-header-collision branch July 17, 2026 15:25
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-17 15:47 UTC Run
prod-us ✅ Deployed 2026-07-17 16:04 UTC Run
prod-eu ✅ Deployed 2026-07-17 16:07 UTC Run

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

Labels

stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant