Skip to content

fix(data-imports): retry Langfuse resource-limit 422s as transient - #72022

Merged
Gilbert09 merged 1 commit into
masterfrom
posthog-code/langfuse-traces-422-retryable
Jul 17, 2026
Merged

fix(data-imports): retry Langfuse resource-limit 422s as transient#72022
Gilbert09 merged 1 commit into
masterfrom
posthog-code/langfuse-traces-422-retryable

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

Error tracking surfaced an uncaught HTTPError from the data warehouse import pipeline, in the Langfuse source:

422 Client Error: Unprocessable Entity for url:
https://<langfuse-host>/api/public/traces?limit=50&fromTimestamp=<redacted>&orderBy=timestamp.asc&page=<deep-page>

Issue: 019f70df-ab1b-7ab1-830c-b70904cf4781. Raised in fetch_page at products/warehouse_sources/backend/temporal/data_imports/sources/langfuse/langfuse.py via raise_for_status(), reached from get_rows while paginating the v1 traces endpoint.

The 422 looks like a client error but isn't one here. Langfuse's public API maps a ClickHouse resource limit (memory limit / overcommit / query timeout) to HTTP 422 with a {"error": "Request timed out"} body. Genuine request-validation failures return 400, not 422. The v1 traces endpoint uses offset-based pagination, and deep offset pages (with the full trace field groups joined) get progressively heavier, so ClickHouse eventually hits a resource/time limit and the request comes back as a 422.

That makes it a transient upstream error, not a credentials/config problem and not a bug in how we build the request (the parameters are valid). But fetch_page only retried 429 and 5xx, so a 422 raised a fatal HTTPError and failed the whole sync instead of being retried.

Changes

Treat a Langfuse 422 as retryable, alongside 429/5xx. It now raises LangfuseRetryableError and flows through the existing tenacity backoff and the resume checkpoint, giving a load- or time-limited request a chance to recover instead of crashing the sync. This is safe because on the Langfuse public API a 422 is exclusively a ClickHouse resource/timeout error — validation errors come back as 400.

I did not widen NonRetryableErrors: this is a transient timeout, so per the pipeline's error-handling policy it stays retryable.

-        if response.status_code == 429 or response.status_code >= 500:
+        if response.status_code in (422, 429) or response.status_code >= 500:

How did you test this code?

Automated only (I'm an agent — no manual sync run). Added test_resource_limit_422_is_retried_then_recovers to TestGetRows: it queues a 422 followed by a valid page and asserts the rows come back and session.get was called twice, proving the 422 was retried rather than surfaced as a fatal error. It catches a regression where 422 handling is dropped and a transient Langfuse timeout again crashes the sync — no existing test exercised the retry-on-status path through get_rows.

Ran the Langfuse source suite locally:

uv run pytest .../sources/langfuse/tests/ -q
72 passed

Invoked the /writing-tests skill while adding the test.

Automatic notifications

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

🤖 Agent context

Autonomy: Fully autonomous

Triaged from an error-tracking webhook by Claude. I confirmed the issue in PostHog error tracking (single occurrence, one team), read the stack trace end to end into fetch_page/get_rows, then traced the 422 into Langfuse's own source: withMiddlewares returns 422 only for ClickHouseResourceError (types MEMORY_LIMIT / OVERCOMMIT / TIMEOUT), while zod validation errors return 400. That's what pinned the decision to "transient, keep retryable" rather than adding it to NonRetryableErrors. I considered tuning page_size, but smaller pages mean deeper offsets for the same data, which doesn't reduce the offset-pagination cost — so the scoped fix is to retry. Checked open PRs (including the maintainer's) for a duplicate; none addresses this.


Created with PostHog Code

Langfuse's public API maps a ClickHouse resource limit (memory/timeout) to HTTP 422
with a "Request timed out" body; genuine request-validation failures return 400. Deep
offset pagination on the traces endpoint can trip that resource limit, so a 422 is a
transient upstream error. fetch_page only retried 429/5xx, so a 422 raised a fatal
HTTPError and failed the whole sync instead of flowing through the existing backoff and
resume checkpoint. Treat 422 as retryable alongside 429/5xx.

Generated-By: PostHog Code
Task-Id: 471e3fa7-0a2b-4575-8c79-dd153f17022f
@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 16:29
@Gilbert09 Gilbert09 added the stamphog Request AI approval (no full review) label Jul 17, 2026 — with PostHog

@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, well-tested fix widening retry handling for a transient Langfuse 422 in the warehouse import connector; diff matches description, author owns this area with strong familiarity, no risky-territory concerns.

  • Author wrote 100% of the modified lines and has 2 merged PRs in these paths (familiarity STRONG).
  • 👍 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, 22L/2F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1b-small (22L, 2F, single-area, fix)
stamphog 2.0.0b3 .stamphog/policy.yml @ 65f0ccb · reviewed head e5ff92b

@Gilbert09
Gilbert09 enabled auto-merge (squash) July 17, 2026 16:45
@trunk-io

trunk-io Bot commented Jul 17, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

@Gilbert09
Gilbert09 merged commit 10bcce2 into master Jul 17, 2026
365 checks passed
@Gilbert09
Gilbert09 deleted the posthog-code/langfuse-traces-422-retryable branch July 17, 2026 17:12
@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 17:38 UTC Run
prod-us ✅ Deployed 2026-07-17 18:01 UTC Run
prod-eu ✅ Deployed 2026-07-17 18:02 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