Repository navigation
Conversation
… limit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A coalesced window whose rendered prompt is too long moves its logged delivery into the suppression bucket when it fails, but only insertion pruned a bucket, so these rows were never trimmed. Prune the row's bucket after a failure update too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This focused server fix closes a retention gap for oversized coalesced webhook failures while preserving the existing bucket limits and failure behavior. A targeted test verifies that the suppression history remains capped at 20 rows. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
Problem
A webhook task with a coalesce key keeps every oversized-prompt failure in its delivery history, past the 20-row limit #22234 set for them.
Webhook delivery history keeps three buckets per task: main (50 rows), filtered (20) and suppression (20). #22234 puts oversized-prompt failures in the suppression bucket so a flood of them cannot push out real runs.
A coalesced delivery escapes that limit. It is logged as
acceptedwhen its window opens, so insertion prunes the main bucket. When the window closes and the rendered prompt is longer than the provider limit,markDeliveryFailedchanges the row todispatch_failedwith "The filled-in prompt is too long." That moves the row into the suppression bucket, but onlyrecordDeliveryprunes, and it prunes the bucket of the row it just inserted. Nothing trims the suppression bucket after the update.So a webhook task with a coalesce key that keeps receiving oversized payloads, one per window, keeps every failed row. Each row keeps up to 64 KiB of the request body. The Deliveries dialog in Settings lists every retained row, so the list grows without bound too.
Steps: create a webhook task whose prompt includes
{{body.text}}, with coalesce key{{body.group}}and a 10-second window. Send a JSON body whosetextis 130,000 characters, once per window, 25 times. On main the Deliveries dialog shows 25 failed rows. The limit is 20.Change
apps/server/src/scheduledTasks/ScheduledTaskService.tsonly.DELETEmove out ofrecordDeliveryintodeliveryBucketandpruneDeliveries, unchanged.recordDeliverycallspruneDeliveriesas before.markDeliveryFailedruns itsUPDATE ... RETURNING task_idand then prunes the row's new bucket, in one transaction. It still ignores errors, as before.The three limits, the received-time and rowid ordering and every caller stay the same. For "The run failed to start." and the other failure messages the row stays in the main bucket, so the extra prune removes nothing new.
Scope and approval
No issue. This is a focused fix of a broken invariant: #22234 documents that oversized-prompt failures share the suppression bucket's 20-row limit, and the immediate path already keeps it. Only the coalesced path, which fails after the row was logged, misses it. One production file and one test.
Verification
Run on Linux. A new test in
ScheduledTaskService.webhook.test.tsdrives 21 coalesced windows with a 120,000-character body through the real service and SQLite, and counts the logged rows. It uses the Effect test clock, like the neighbouring coalescing tests.On main the test fails with 21 rows kept. On this branch it passes.
Real app on Linux: production web and server builds of main and of this branch, each server with a fresh data directory on loopback, driven from Chromium. In each, I added an empty project and created the task above in Settings > Scheduled Tasks. Then I sent 25 oversized requests, each in its own coalescing window, 13 seconds apart. All 25 got HTTP 202, and no run started.
dispatch_failedrows in SQLiteOn main the dialog lists all 25 failed deliveries, oldest 6 minutes ago:
On this branch it keeps the newest 20:
Also run on this branch, in
apps/server:vp test run src/scheduledTasks: 8 files, 80 tests pass.tsc --noEmit: exit 0, no errors.vp fmt --checkandvp linton both changed files: pass. Lint prints one existing warning in an untouched test at line 521.Not checked:
Prior PRs: #22234 by @juliusmarminge added the three retention buckets and the suppression rule for oversized prompts. This PR reuses its bucket predicate and
DELETEunchanged. Open #23579 by @juliusmarminge moves the pending-window drain that callsmarkDeliveryFailed; it does not change the helper this PR edits, and the two merge cleanly.Production fix, tests, PR description and images written with Claude Opus 5.5 in T3 Code Nightly.
🤖 Generated with Claude Opus 5.5 in T3 Code Nightly