Skip to content

fix(warehouse): surface actionable error on postgres incremental field type drift - #68293

Closed
posthog[bot] wants to merge 1 commit into
masterfrom
posthog-code/postgres-incremental-field-type-drift
Closed

fix(warehouse): surface actionable error on postgres incremental field type drift#68293
posthog[bot] wants to merge 1 commit into
masterfrom
posthog-code/postgres-incremental-field-type-drift

Conversation

@posthog

@posthog posthog Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Problem

A Postgres warehouse source configured for incremental sync on a column whose type
was altered after setup (for example a timestamp column later changed to text)
gets permanently, silently stuck. The stored incremental_field_type no longer
matches the live column, so _build_query emits WHERE "col" > '<timestamp>' and
Postgres rejects it with operator does not exist: text > timestamp without time zone.
Because that message contains does not exist, it's classified non-retryable and the
sync gives up — the customer only sees the raw Postgres error with no guidance on how
to fix it, and the sync stays halted until someone reconfigures the incremental field.

Changes

  • Re-validate at sync setup. Before building any query, compare the persisted
    incremental field type against the live column type (_validate_incremental_field_type_matches_column).
    On drift, fail fast with an actionable IncrementalFieldTypeMismatchError telling the
    user to reconfigure the incremental field. Compatible cross-type-same-category cases
    Postgres handles fine (e.g. a date column with a timestamp cursor) are not flagged;
    xmin (a synthetic cursor) is excluded.
  • Map the reactive case too. Add an operator does not exist entry to
    PostgresSource.get_non_retryable_errors, ordered before the generic does not exist
    key so its actionable message wins, as a fallback for any comparison that still reaches
    Postgres.

How did you test this code?

Automated tests only (added to test_postgres.py); no manual sync run was performed.

  • TestValidateIncrementalFieldTypeMatchesColumn — parameterized over compatible
    types (that must not raise, including datetimestamp and smallint/bigintinteger)
    and incompatible drift (text/varchar vs a temporal/numeric cursor, cross-category
    swaps), plus the missing-column and xmin skip paths. Catches a regression where drift
    detection either fails to fire on a text-vs-timestamp column or false-positives on a
    still-compatible column.
  • TestPostgresSourceNonRetryableErrors additions — assert the mismatch (both raw and
    Temporal-wrapped forms) is non-retryable, and that the operator does not exist key is
    ordered ahead of does not exist so production's friendly_errors[0] surfaces the
    actionable message rather than None.

Ran the two classes locally: 115 passed.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

🤖 Agent context

Autonomy: Fully autonomous

Authored by Claude Code (Opus 4.8) from a PostHog inbox report. Invoked the
/writing-tests skill before adding tests to apply the value gate (each test group
names the regression it catches). Considered two approaches from the report — a mapped
user-facing message for the operator does not exist case, and proactive re-validation
at setup — and implemented both: the early validation prevents the mismatched query from
ever reaching Postgres, and the mapped message is a defense-in-depth fallback. Category
grouping (temporal/numeric) was chosen over exact-type equality so legitimate implicit
casts Postgres already accepts (date vs timestamp) don't get falsely rejected.


Created with PostHog Code from an inbox report.

…d type drift

When a Postgres incremental field's column type is altered after the sync is
configured (e.g. a timestamp column changed to text), _build_query still emits a
literal of the stored incremental_field_type. Postgres rejects the comparison with
"operator does not exist: text > timestamp without time zone", which is classified
non-retryable and permanently halts the sync behind a cryptic error with no guidance.

Re-validate the persisted incremental field type against the live column type at sync
setup and fail early with an actionable message, and map the reactive
"operator does not exist" case in get_non_retryable_errors as a fallback.

Generated-By: PostHog Code
Task-Id: 618e3d05-0467-4122-9417-e0f26713be59
@posthog posthog Bot added the skip-agent-review Save $$$, skip auto agent reviews (Greptile) — use for trivial or chore PRs label Jul 3, 2026
@scheduled-actions-posthog

Copy link
Copy Markdown
Contributor

This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the stale label – otherwise this will be closed in another week. If you want to permanently keep it open, use the waiting label.

@scheduled-actions-posthog

Copy link
Copy Markdown
Contributor

This PR was closed due to lack of activity. Feel free to reopen if it's still relevant.

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

Labels

skip-agent-review Save $$$, skip auto agent reviews (Greptile) — use for trivial or chore PRs stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants