Repository navigation
fix(api): preserve marketplace listing state when compensating cancellation fails #335
Description
Summary
The existing marketplace listing path reports that a listing was rolled back and releases its local claim even when compensating AWS cancellation fails. The external listing can remain active while the local row no longer records the new listing.
Found during independent final-HEAD review of LeanerCloud/cloud-commitments-cli#2077 at 899ab790d40cf4bf0475edd7cd32ce2f21481186; the same code is present in its base 81f2fc3ac44ce47221682fa1c50dec26e4f24eed. The IAM PR does not modify this handler.
Current behavior
internal/api/handler_marketplace.go:265-276, reserveAndCreateListing:
- AWS listing creation succeeds and returns a listing ID.
UpdatePurchaseHistoryListingfails to persist that result.- The compensating
CancelMarketplaceListingalso fails. - The handler logs cancellation failure, calls
releaseMarketplaceClaimanyway, and returns an error saying the listing has been rolled back.
If claim restoration succeeds, it restores the old listing fields despite the unresolved external listing. The response is inaccurate even when restoration fails. A later create attempt uses a fresh client token; whether AWS accepts another listing is not established here and no duplicate listing is claimed as an observed result.
Steps to reproduce safely
Use the existing handler/mock test infrastructure with a Standard RI, authorized session/account, successful claim and AWS create, then inject a DB result-save failure and a cancellation failure. Allow claim restoration to succeed. Assert returned error, stored listing state, call order, and retry behavior. Do not create a real marketplace listing to reproduce this.
Expected behavior
Report cancellation/rollback uncertainty honestly and keep enough ownership/state to reconcile the known external listing. Do not restore a safely-retryable local state while cancellation is unconfirmed. Also preserve correct behavior when compensation succeeds and when the database remains unavailable.
Proposed fix
Fix the failure branch in internal/api/handler_marketplace.go using the existing store/listing state model, then add handler-level regression coverage for create success plus DB failure plus cancellation failure. Verify the complete response and persisted state, not only the helper error. Decide the minimal recoverable-state treatment before changing the store contract; do not assume the unimplemented status poller already handles this path.
References
- Parent review: fix(iac/aws): grant six required runtime CE and EC2 actions cloud-commitments-cli#2077, related IAM issue fix(iac/aws): RI Marketplace listing actions are granted in no IaC flavor cloud-commitments-cli#1968.
- Existing marketplace status-poller follow-up: RI Marketplace: status poller (active->closed + proceeds/fee) + seller-account precondition UI (closes #292 remainder) #27, explicitly excludes creation/cancellation.
- Ambient account credential selection is separately tracked in fix(api): marketplace list/cancel use the host's ambient AWS credentials for any account #261.
- chore(api): medium/low findings from the 2026-07-28 full review #100 covers AWS error classification, not this compensation branch.
- No matching marketplace compensation entry was found in
known_issues/or the searched open issues.
Severity
Medium, P2, this sprint, few users, small effort, bug. The reachable double-failure path can leave an externally active listing untracked and falsely report rollback. This is confirmed by source tracing, not a live AWS failure experiment.
Activity