Skip to content

bug(purchases): partial-failure leaves whole execution 'failed' + skips success notification (backend) and orphans pending executions (fan-out) #642

Description

@cristim

Partial-failure across the purchase flow is not handled cleanly (two layers)

When some recs in a multi-rec / multi-account / fan-out purchase succeed and others fail, the flow leaves inconsistent state.

(a) Backend: whole execution marked failed + success notification skipped despite real commitments

  • internal/purchase/execution.go:74-80 (single-account) returns early when len(purchaseErrors) > 0, so sendPurchaseNotification is skipped even though the successful recs already fired and were written to purchase_history with Purchased=true.
  • finalizeExecution (internal/purchase/manager.go:128-137) is binary (completed XOR failed), so the row is marked failed even though real commitments were created.
  • executeForAccount (execution.go:129-148, multi-account) has the same shape.

Result: the user gets no confirmation email for commitments that actually purchased, and History/status shows failed for a partially-successful run (misleading on a financial action, and invites a re-attempt that would double-buy on the already-purchased recs).

(b) Frontend: fan-out partial failure leaves orphaned pending executions

handleFanOutExecute (frontend/src/app.ts:445) uses Promise.allSettled over N per-bucket executePurchase POSTs. If bucket 3 of 5 fails, buckets 1-2 are already-created pending executions that approvers can act on, with no compensating cleanup — only a toast reporting counts.

Fix

  • Backend: introduce a partially_completed outcome (or keep completed but record per-rec failures) so the row reflects reality, and send the success notification for the recs that did purchase. Never mark a row failed when commitments were created (re-purchase / double-spend hazard).
  • Frontend: on fan-out partial failure, surface which buckets created pending executions and offer to cancel the orphaned ones (or document that they remain actionable).
  • Regression tests for both layers.

Dedup

Distinct from #632 (approved-strand) and #621 (vanish). No existing issue covers partial-success status/notification or fan-out orphan cleanup. New. Surfaced in the full purchase-workflow trace.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions