Skip to content

fix(api): automation pruner passed its provider list as one bind parameter - #6756

Merged
saddlepaddle merged 1 commit into
mainfrom
fix/automation-pruner-provider-filter
Aug 22, 2026
Merged

saddlepaddle merged 1 commit into
mainfrom
fix/automation-pruner-provider-filter

Conversation

@saddlepaddle

@saddlepaddle saddlepaddle commented Aug 22, 2026 •

Copy link
Copy Markdown
Collaborator

The automation payload pruner has failed on every run since it deployed — 811 errors in 24h, last one two minutes before I opened this. It has never pruned a row.

Failed query: WITH batch AS ( SELECT id, received_at FROM automation_events
WHERE payload IS NOT NULL AND received_at < now() - $1::interval
AND provider <> ALL(($2)) ...

provider <> ALL(${PAYLOAD_READBACK_PROVIDERS}) sends the JS array as a single bind parameter. Postgres receives it as a record and won't cast it to text[].

Fixed by building an explicit list with sql.join, the same way ACTIVE_SUBSCRIPTION_STATUSES is handled in load-custom-session-data.ts.

Verified against a copy of production

  • Plans as Index Scan using automation_events_prunable_idx, no sort
  • One batch executes in 50ms

It prunes 0 rows today, which is correct rather than broken: automation_events was created on 2026-08-15 by migration 0071, so nothing is older than the 7-day window yet. It has work from tomorrow.

Related correction

I previously described automation_events as having been accumulating unpruned for a long time. It's 7 days old. That makes the growth rate worse than I said, not better — 35 GB in 7 days, ~5 GB/day, with nothing deleting it. The 4.55M rows currently in it are all within the retention window.

Why this reached production

I hit the identical array-binding failure in a throwaway script earlier the same day and fixed it there, then shipped this route without ever executing it against a database. The partitioning work beside it was validated end-to-end against a real production copy; this route was not. The check that would have caught it is the one I ran today: execute the actual statement against a real branch before merging.

https://claude.ai/code/session_014D6wif8HDRCXEFf5Ku4w7V


Summary by cubic

Fixes the automation payload pruner by replacing provider <> ALL($1) with an explicit NOT IN (...) list built via sql.join. The old query bound the provider array as one parameter, which Postgres rejected, so every run failed; the new query runs successfully.

  • Review notes:

    • Filter semantics are unchanged; we still exclude ["google_calendar"].
    • We build the exclusion list with sql.join, emitting parameterized values to avoid the array-binding bug.
  • Operational notes:

    • On a production copy, the query plans as an index scan on automation_events_prunable_idx and completes in ~50 ms per batch.
    • No rows prune today: automation_events is 7 days old and the retention window is 7 days. Expect first deletions starting tomorrow.

Written for commit c5d614c. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved automation payload cleanup by correctly excluding configured providers during pruning.
    • Ensured eligible payloads are identified more reliably for removal.

…meter

`provider <> ALL(${PAYLOAD_READBACK_PROVIDERS})` sends the JS array as a
single parameter, which Postgres receives as a record and refuses to cast
to text[]. Every run since deploy has failed on it — 811 errors in 24h,
so the job has never pruned a row.

Built as an explicit list instead, the same way the active-subscription
statuses are handled in load-custom-session-data. Verified against a copy
of production: the statement now plans as an index scan on
automation_events_prunable_idx and executes in 50ms.

It prunes nothing today, which is correct rather than broken:
automation_events was created on 2026-08-15 by migration 0071, so no row
is yet older than the 7-day window. It has work from tomorrow.

Worth recording why this reached production: I hit the identical array
binding failure in a throwaway script earlier the same day and fixed it
there, but shipped this route without ever executing it against a
database. The partitioning work beside it was validated end to end; this
was not.

Claude-Session: https://claude.ai/code/session_014D6wif8HDRCXEFf5Ku4w7V
@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1657f027-f917-4ddf-b7aa-9dac384d42ce

📥 Commits

Reviewing files that changed from the base of the PR and between 83338ee and c5d614c.

📒 Files selected for processing (1)
  • apps/api/src/app/api/automations/jobs/prune-payloads/route.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The prune-payloads route now builds individually bound SQL values for excluded providers. The pruning query uses these values in a NOT IN clause instead of the previous array-based <> ALL filter.

Changes

Prune Payload Provider Filtering

Layer / File(s) Summary
Provider exclusion SQL
apps/api/src/app/api/automations/jobs/prune-payloads/route.ts
The route expands excluded providers into bound SQL values. The pruning query uses the resulting values in NOT IN.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to c5d61

This change fixes provider filtering so the payload pruner can execute correctly. It is localized to one route, and no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses conventional commit format and clearly identifies the provider-list binding fix in the automation pruner.
Description check ✅ Passed The description explains the problem, fix, testing, operational impact, and production cause; only the template checklist is omitted.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/automation-pruner-provider-filter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

@saddlepaddle
saddlepaddle merged commit e8842aa into main Aug 22, 2026
20 checks passed
@saddlepaddle
saddlepaddle deleted the fix/automation-pruner-provider-filter branch August 22, 2026 15:39

This branch was previously deployed

1 inactive deployment
preview — c5d614c2 Deployed Aug 22, 2026 by saddlepaddle via Deploy Admin #8277
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant