fix(data-imports): stop duckgres enablement crashing on non-UUID control-plane org ids - #74082
Merged
Gilbert09 merged 2 commits intoJul 28, 2026
Merged
Conversation
…rol-plane org ids The duckgres batch sink's per-poll enablement refresh filtered DuckgresServer rows by the managed-warehouse control plane's raw organization_id strings, which aren't guaranteed to be valid UUIDs (the control plane has rows keyed by human-readable slugs, e.g. for internal test/dev servers). Passing one of those straight into a Django ORM filter against a UUID foreign key raised a ValidationError and aborted the whole refresh for every team, not just the mismatched row. Filter by the app DB's own resolved organization ids instead, which are already guaranteed to be valid UUIDs. Branch: posthog-code/fix-duckgres-enablement-uuid-crash Generated-By: PostHog Code Task-Id: 94dc4a2d-02ac-452b-89cf-41e90a414c50
Contributor
|
Hey @Gilbert09! 👋 It looks like your git author email on this PR isn't your
You can fix it for this repo with: git config user.email "you@posthog.com"Or set it globally with |
There was a problem hiding this comment.
Small, well-scoped reliability fix: swaps an unvalidated control-plane string into the UUID FK filter for the app DB's own already-validated org ids, preventing a crash; includes a regression test and matches its description. Not risky territory, author is on the owning team.
- 👍 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 | ✓ | 6L, 1F substantive, 32L/2F incl. docs/generated/snapshots — within ceiling |
| tier | ✓ | T1-agent / T1b-small (32L, 2F, single-area, fix) |
| stamphog 2.0.0b3 | .stamphog/policy.yml @ f11de4a · reviewed head 6ff807d |
…crash Generated-By: PostHog Code Task-Id: 25b1a6d9-ec9d-44f1-8eeb-5ed3825dda0b
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Error tracking surfaced a
ValidationError: "%(value)s" is not a valid UUID.raised fromduckgres_sink_enablement()inproducts/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/duckgres/enablement.py, called from the batch sink consumer's_enabled_team_ids().duckgres_sink_enablement()reads every team row from the managed-warehouse control plane, then queriesDuckgresServerto fetch each org's sink concurrency budget:row.organization_idis a raw string reported by the control plane, and the control plane isn't guaranteed to report a UUID there — it has rows for internal test/dev servers keyed by human-readable slugs instead.organization_idis a UUID foreign key onDuckgresServer, so passing a non-UUID value into__inmakes Django try to validate every value while building the query, and it raises before ever reaching the org-id match-up a few lines down that would otherwise have filtered the row out. One bad row anywhere in the control plane's response aborted the enablement refresh fleet-wide.The sampled exception events all lacked any
warehouse_sources_*sync context, consistent with the failure coming from this background enablement refresh rather than any specific source/schema/sync.Changes
enablement.py: build theDuckgresServerfilter from the app DB's own resolved organization ids (team_info, already validated UUIDs from theTeamtable) instead of the control plane's raworganization_idstrings. Rows for orgs the app DB doesn't recognize already get skipped by the existing org-id match-up right after; they just shouldn't be able to crash the query that runs before it.How did you test this code?
uv run ruff check/ruff format --check— clean.uv run mypy --cache-fine-grained .(scoped to the two changed files) — clean.test_duckgres_sink_enablement_ignores_non_uuid_control_plane_org_idstotest_enablement.py: two control-plane rows for the same team, one with a valid org id and one with a non-UUID slug (mirroring the real control-plane data that triggered this). Asserts the refresh doesn't raise and still resolves the valid team.django.core.exceptions.ValidationError: ['"value" is not a valid UUID.']) at the same call site; against the fix, the same script resolves the enablement result without error.pytestitself for this file needs the full ClickHouse cluster config (shard/replica macros) that the dev docker-compose stack provides, which the standalone repro didn't need since it doesn't touch ClickHouse.Docs update
N/A — internal reliability fix to a background cache refresh, no user-facing or documented behavior change.
🤖 Agent context
Autonomy: Fully autonomous
I (Claude, via PostHog Code) triaged this from a PostHog error-tracking issue with no human steering the investigation. Pulled the issue's stack trace, impact, and sampled
$exceptionevents (including captured code variables showing the actual control-plane row values) via the PostHog MCP error-tracking tools, confirmed the sampled events all lackedwarehouse_sources_*sync context, then readenablement.pyto trace the raise site. The capturedrowsvariable on the exception events showed the exact non-UUIDorganization_idvalues from the control plane, which made the root cause unambiguous.Checked for duplicates: searched open PRs by exception message, module path, and function/file name, and reviewed the maintainer's (Gilbert09) own open PRs. Found two related-but-distinct PRs in this same file (#73975, #73995) that both fix a different bug — the
RuntimeErrorraised when the control plane is unreachable (rows is None) causing a retry storm — neither touches this UUID-filter crash, so this isn't a duplicate.Invoked
/writing-testsbefore adding the regression test.