Skip to content

Async Transaction Error Codes

When a transaction reaches failed, the callback (and the Payment Status response) carries an errorCode and errorMessage. These differ from the synchronous errors returned at request time (those are documented per-endpoint and in Error Handling) — async error codes describe why a transaction that was successfully created later failed.

This page is the catalogue. Use it to:

  • Decide what to display to the end user.
  • Decide whether the transaction can be safely retried with a fresh merchantReference.
  • Map provider-specific errors to a stable, documented vocabulary.

Provider's native code is also available

The providerData.errorCode and providerData.errorMessage fields carry the raw code returned by the underlying provider. Use the gateway-normalised errorCode as your primary signal — fall back to providerData only when you need provider-specific detail.


How to Read This Catalogue

Column What it tells you
Error code The string in the errorCode field. Treat as opaque — do not parse the prefix.
What happened The condition that caused the failure.
User-facing message A short, suggested phrasing you can show the end user. Adapt to your tone.
Retryable? Whether asking the user to try again with a new merchantReference is likely to succeed. "No" means the failure is structural; retrying without changing inputs will fail the same way.

Treat any code not listed here as not retryable and surface a generic error.


User-Side Failures

Caused by something on the end user's side — funds, limits, authentication, intent.

Error code What happened User-facing message Retryable?
user_insufficient_funds The end user's wallet did not have enough money. "Insufficient funds. Please top up and try again." Yes — after the user adds funds.
user_limit_exceeded The end user has hit a daily/weekly/monthly account limit. "You've reached your limit for this period. Try again later or contact your provider." Maybe — usually next period.
user_authorization_failed The end user entered a wrong PIN/OTP, or authentication failed. "We couldn't authenticate this payment. Please try again." Yes.
user_timeout The end user did not approve the prompt in time (STK push expired). "The payment prompt timed out. Please try again." Yes.
user_invalid_msisdn_for_method The MSISDN you submitted is not registered for this payment method. "This phone number isn't enrolled for the selected payment option." No — the user must pick a different method or fix the number.
user_account_block The end user's account is blocked by the provider. "We can't process payments for this account. Please contact your provider." No.
user_too_many_requests The end user has triggered too many attempts in a short window. "Too many attempts. Please wait a few minutes and try again." Yes — after a delay.
user_cancelled The end user cancelled the prompt. "Payment cancelled." Yes.
user_unknown_error Provider reported an unspecified user-side error. "Something went wrong on the user's side." Maybe.

Provider-Side Failures

The end user did everything right; the upstream provider rejected the transaction or didn't respond.

Error code What happened User-facing message Retryable?
provider_unavailable The provider's system was down or unreachable. "Payment provider is temporarily unavailable. Please try again shortly." Yes — after a delay.
provider_timeout The provider didn't respond within the gateway's wait window. Same as above. Yes.
provider_noresponse_from_method The specific payment method didn't return a status. Same as above. Yes.
provider_connection_error Network-level error contacting the provider. "Connection issue with payment provider. Try again." Yes.
provider_too_many_requests The provider rate-limited us. "We're seeing high traffic. Try again in a moment." Yes — backoff.
provider_bad_request The provider rejected the request shape — usually a configuration mismatch. "We couldn't process this payment. Please contact support." No — needs investigation.
provider_wrong_configuration The provider returned a configuration error. Same as above. No.
provider_transactionid_duplicate The provider's own dedup window rejected the reference. "Payment reference already used at the provider. Please retry with a fresh attempt." Yes — with a new merchantReference.
provider_cancelled The provider cancelled the transaction (anti-fraud, manual review, etc.). "Payment was cancelled by the provider." No.
provider_limit_exceeded The provider's per-transaction or daily limit applied. "Payment exceeds the limit allowed by the provider." Maybe — try a smaller amount or later.
provider_unknown_error Provider returned an error we couldn't classify. "Payment failed. Please try again." Maybe.

Risk and Authentication

Error code What happened User-facing message Retryable?
risk_blocked Risk engine blocked the transaction. "Payment was declined for security reasons." No.
security_authentication_failed The credentials presented to the provider were rejected. "Payment failed. Please contact support." No.
security_authorization_failed The caller does not have permission for this operation. Same as above. No.

Configuration Failures

These usually indicate a setup mismatch on PayAlo's side. They should be rare in production for a well-configured brand; report them to support if you see them.

Error code What happened Retryable?
config_unsupported_currency The currency is not configured for this method/brand. No — fix configuration.
config_unsupported_country The country is not configured. No.
config_unsupported_payment_method The method is not enabled for this brand. No.
config_method_route_not_found No route exists for the method/country/currency triple. No.
config_method_transaction_min_limit Amount below the configured minimum. Yes — with a higher amount.
config_method_transaction_max_limit Amount above the configured maximum. Yes — with a lower amount or split.
config_credentials_missing The brand's provider credentials are missing. No — contact support.

Merchant-Side Failures

Error code What happened Retryable?
merchant_disabled The brand has been disabled. No — contact your account manager.
merchant_insufficient_funds The merchant float used for pay-outs has insufficient balance. Yes — after topping up.
merchant_limit_exceeded The merchant has hit a configured limit. Maybe.
merchant_invalid_credentials The merchant credentials at the provider are invalid. No — contact support.
merchant_cancelled_by_risk A risk rule cancelled this transaction. No.
merchant_transactionid_duplicate Duplicate merchantReference. (Usually returned synchronously, but may also appear async.) Yes — with a new reference.
merchant_unknown_error Merchant-side error we couldn't classify. Maybe.

Gateway and Lifecycle

Error code What happened Retryable?
transaction_expired The gateway-side timeout fired (a pending transaction sat without resolving for too long). The transaction is auto-marked failed. Yes — with a new merchantReference.
transaction_not_found Status lookup failed because the transaction does not exist for this brand. No.
gateway_too_many_requests The gateway rate-limited the request. Yes — backoff.
hpp_open_timeout The hosted payment page session was created but never opened in time. Yes.

Recommendations

  • Log the full callback payload when handling a failure, including errorCode, errorMessage, and providerData. Provider context is often the only thing that explains the failure after the fact.
  • Don't show provider error messages to users verbatim. They're inconsistent across providers and often unhelpful. Map errorCode to your own copy.
  • Don't retry without changing the merchantReference. Even on retryable codes, the original merchantReference is consumed and will hit the duplicate guard.
  • Treat unknown codes as not-retryable. New codes may be added in future; failing closed is safer than failing open.