Errors, Logging and Troubleshooting
WirexError codes and reasons, logging, timeouts and the common failures of the iOS SDK.
Before You Start
Read the following guides before proceeding:
Where Errors Arrive
| Surface | Delivery |
|---|---|
| Reveal | snapshot.state .error(WirexError). load() throws only WirexDeclined or CancellationError; session(cardId:…) throws WirexError with reason invalidCardId |
| Collect | tokenizeTopUp / tokenizeOctDestination throw WirexError |
| 3-D Secure | outcome.error, code originRefused, declined or unavailable |
| Apple Pay | .executeFailed(WirexWalletExecuteError) and .failed(WirexWalletError) events |
WirexError
WirexErrorcode | Handling |
|---|---|
unauthorized | Refresh the access token. The SDK never retries a 401 |
actionRequired | Obtain a fresh action token and retry |
updateRequired | Ship a newer SDK build |
invalidInput | Fix the input — see reason |
temporary | Retry when retryable is true |
failed | Terminal |
retryable says whether a retry can help. reason adds detail where the API supplies one:
reason | Meaning |
|---|---|
actionTokenRejected | The API rejected the action token. Mint a fresh GetCardDetails token |
cardAlreadyLinked | Collect: the card is already linked |
cardLimitReached | Collect: the user holds the maximum number of saved cards |
invalidCardId | Reveal: the card id is malformed |
e.detail.apiCode carries the API's own error code where present.
Other Types
WirexDeclinedis not an error state: the user said no and nothing was sent.WirexEnvironmentUnavailableis thrown byWirexEnvironment.resolve(_:chainId:)and
WirexWalletEnvironment.resolve(_:):unknownName,notBuilt,malformedEndpoints,missingChainId. The
create(...)factories returnnilfor the same conditions. Show the reason; never fall back to another
environment.
func describe(_ error: Error) {
if let e = error as? WirexError {
print(e.code, e.retryable, e.reason as Any, e.detail.apiCode as Any)
} else if let e = error as? WirexEnvironmentUnavailable {
print(e)
} else if error is WirexDeclined {
print("user declined the step-up")
}
}Logging
Logging is off by default. Every logged value passes a redactor; tokens and card data are never handed to a
sink.
| Surface | Option |
|---|---|
| Reveal, Collect | logging: WirexLogging(level:sink:) on create(...). Presets: .disabled, .debug, osLogLogging(level:) |
| Apple Pay | WirexWalletLogging(level:sink:) with its own WirexWalletLogSink protocol |
final class ConsoleLogSink: WirexLogSink {
func write(level: WirexLogLevel, event: String, fields: [String: String]) { print(level, event, fields) }
}
let reveal = WirexReveal.create(
credentials: credentials, chainId: "<chain id>",
logging: WirexLogging(level: .info, sink: ConsoleLogSink()),
timeouts: WirexTimeouts(requestSeconds: 20, resourceSeconds: 60))Log at debug in development builds and warn in release.
Timeouts
timeouts: WirexTimeouts(requestSeconds:resourceSeconds:) on create(...) for Reveal and Collect.
Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
create(...) returns nil | The build has no endpoints for that environment, or chainId is empty | Call WirexEnvironment.resolve(_:chainId:) to get the reason |
Reveal or Collect fails with unauthorized | Stale bearer. The SDK never retries a 401 | Check expiry and refresh inside accessToken() |
API answers 400 on every call | Both userEmail and userWallet are set | Return exactly one identity |
| A revealed field is blank | Simulator secure layer, or a box shorter than naturalHeight | Test on a device; size the box to naturalHeight |
| Collect cannot tokenize | Billing details missing, or the user is not enrolled at the acquirer | Send every required billing field — see Collect a Card |
| The Pay button is unavailable | Checker not started, empty provider block, or the Simulator | Start WirexWalletAvailabilityChecker with the real block; test on a device |
3-D Secure ends in .pending | The payment is not settled yet | Not an error. Settle from the top-up status |
Updated about 3 hours ago
Did this page help you?

