---
title: "What does Transaction.RefundRequestError.failed mean?"
description: "Transaction.RefundRequestError.failed means a StoreKit 2 refund request could not be submitted, for example because the transaction ID is not valid. Check the ID and the verified transaction."
url: https://revenuedot.app/docs/errors/storekit-refund-error-failed
---

# What does Transaction.RefundRequestError.failed mean?

Transaction.RefundRequestError.failed means the App Store could not take the refund request you submitted.

## Quick facts

| | |
|---|---|
| Error | `Transaction.RefundRequestError.failed` |
| Where | StoreKit 2, Swift (`Transaction.beginRefundRequest(in:)`) |
| Available since | iOS 15.0, iPadOS 15.0, macOS 12.0, tvOS 17.0, watchOS 10.0, visionOS 1.0 |
| What the vendor says | The refund request submission failed. |

## Cause

- Apple says a submission can fail for many reasons, such as an invalid transaction identifier, or because the App Store cannot process the request for another reason.
- It is different from `duplicateRequest`, which means a request for the transaction already exists.
- The refund sheet needs a transaction the App Store knows, so passing the ID of an unverified or unrelated transaction fails.

## Fix

1. Get the transaction with `Transaction.latest(for:)` and only offer the request for a `.verified` result.
2. Pass that transaction's own `id` and a scene that is on screen.
3. Tell the person the request could not be sent and let them try again later. Retrying at once is unlikely to help.
4. Log the error and transaction ID. Refund requests can be tested in the sandbox, as Apple's refund testing guide describes.

## Example

```swift
import StoreKit
import UIKit

// A failed submission: say so, and offer a later retry.
@MainActor
func requestRefund(productID: String, in scene: UIWindowScene) async {
    guard let result = await Transaction.latest(for: productID), case .verified(let transaction) = result else {
        print("No verified transaction to refund")
        return
    }
    do {
        let status = try await transaction.beginRefundRequest(in: scene)
        print("Refund request status: \(status)")
    } catch Transaction.RefundRequestError.failed {
        print("The refund request could not be submitted; try again later")
    } catch {
        print("Refund request error: \(error)")
    }
}
```

*Compile-checked: `npm run check:snippets` type-checks this snippet with `swiftc` against the Apple SDK (macOS target, checked 2026-10-03).*

## How the RevenueDot SDK reports it

The RevenueDot `purchases-ios` fork wraps this in `beginRefundRequest(forEntitlement:)`. It logs "Refund request submission failed" and reports the failure as `ErrorCode.beginRefundRequestError` (code 31).

## Related

- [What does Transaction.RefundRequestError.duplicateRequest mean?](https://revenuedot.app/docs/errors/storekit-refund-error-duplicate-request.md)
- [What does storeProblemError (code 2) mean in the purchases SDK?](https://revenuedot.app/docs/errors/sdk-store-problem-error.md)
- [What does VerificationError.invalidSignature mean in StoreKit 2?](https://revenuedot.app/docs/errors/storekit-verification-error-invalid-signature.md)

## Source

- [Apple: Transaction.RefundRequestError.failed](https://developer.apple.com/documentation/storekit/transaction/refundrequesterror/failed)
- [Apple: Transaction.RefundRequestError](https://developer.apple.com/documentation/storekit/transaction/refundrequesterror)
- [RevenueDot purchases-ios: SK2BeginRefundRequestHelper.swift](https://github.com/revenuedot/purchases-ios/blob/revenuedot/main-patches/Sources/Purchasing/StoreKit2/SK2BeginRefundRequestHelper.swift)

Checked: 2026-10-03
