SDK Error Codes

This reference covers com.koard:koard-android-sdk:1.0.7, published on Maven Central. The release AAR matches the artifact distributed in the public Android repository. For device recovery steps, see Troubleshooting.

Reading an SDK Error

Catch com.koardlabs.merchant.sdk.domain.exception.KoardException and inspect its error, a com.koardlabs.merchant.sdk.domain.KoardError:

Value Meaning Use
exception.error.errorType A KoardErrorType classification Choose a recovery action by matching the concrete type
exception.error.shortMessage Human-readable context Retain a redacted diagnostic message; do not parse it as a stable code or display an unfiltered API response body
HttpError.errorCode HTTP response status, when this specific subtype is present Handle HTTP 401/403, 404, 429, or server failures as appropriate
exception.cause Optional cause for some error paths Diagnostic context only; wrapped reader failures may omit the underlying cause

Read errorCode only after matching error.errorType as KoardErrorType.KoardServiceErrorType.HttpError.

There is no general numeric code property on KoardError or KoardException. The reader codes below describe diagnostic conditions handled by the released SDK. They are not fields you can read back from every exception. Use the Koard type for control flow and keep a fallback for unmapped codes (GeneralError). Wrapped reader errors use Koard descriptions; their messages do not necessarily retain the numeric code or raw engine detail.

Thrown Errors, Result Failures, and Reader Events

Channel Handling
Thrown KoardException Catch around the SDK invocation as well as collection. Readiness can fail before a transaction Flow is returned
Failed Result<T> Inspect the failure or call getOrThrow() inside try/catch. This applies to APIs such as reference-based capture/refund
Emitted KoardTransactionResponse Inspect nullable actionStatus, finalStatus, statusCode, and transaction; see SDK Response Codes

The Activity-based tap refund returns a Flow, while the reference-based refund returns Result<KoardTransaction>. A returned value or OnComplete event does not by itself establish payment success. Use the complete Running Payments handler for transaction status, partial approvals, surcharge decisions, and recovery when no terminal event arrives.

A network failure or timeout after submission can leave a payment outcome unknown. Retrieve and reconcile the existing transaction before another sale, capture, refund, or confirmation. Error handling must not automatically replay a payment. Preserve coroutine cancellation, run SDK work on a worker dispatcher, and update the UI on Main.

KoardErrorType Reference

KoardErrorType is a sealed hierarchy. The following tables list Koard-facing types your app can handle directly in the published 1.0.7 AAR. Additional reader conditions are described by their diagnostic codes below; use a fallback handler for other wrapped errors. Names in each table are relative to KoardErrorType or the nested family named in the heading. Keep a fallback branch for future types and unexpected exceptions.

Top-Level Error Types

Error Type When It Occurs
GeneralError Catch-all for unmapped or unexpected errors
CertificateError TLS or certificate validation failure
BLEError Bluetooth/peripheral communication failure
MainThreadError SDK method called on the main thread (must use a worker thread)
NfcTransactionError NFC transaction-level failure
DeviceNotProvisionedError Device has not been provisioned for Tap to Pay

NotReady: Blocked Before the Tap Starts

sale(), preauth(), completePartialAuth(), and refundEmv() refresh their readiness checks first and throw a KoardException whose error.errorType is a KoardErrorType.NotReady.* value if the SDK cannot transact. The Flow is never returned, so wrap both the call and collection in try / catch. KoardSdkReadiness.notReadyReason() returns the same typed reason without attempting a transaction.

Error Type Meaning What to do
KernelAppNotInstalled The Visa Tap to Pay Ready app isn't installed Route the user to install it (installKernelApp(activity))
DeveloperModeEnabled Developer options are on Prompt the operator to disable developer mode
NotAuthenticated No merchant session Call login(...)
NotEnrolled The device has not been enrolled Set an active location, then call enrollDevice()
NoActiveLocation No active location selected Call setActiveLocation(locationId)
ReaderNotStarted Enrolled with a location, but the reader connection was never started or was killed Call prepare() to bring the session back up before retrying the tap
Preparing Enrolling, generating certificates, the payment processor is coming up, or a location switch is in flight Transient: wait for preparation and check readiness again before offering a tap
CertificateFailed(error) Certificate generation failed Inspect the associated error string; re-run refreshDeviceCertificates()
EnrollmentFailed(error) Enrollment failed Inspect the associated error string; see Troubleshooting
PaymentProcessorFailed(error) The reader service failed to start Inspect error; resetKernelService() to release the IPC connection, then retry (optionally prepare() first to re-warm)

KoardServiceErrorType: API / HTTP Errors

Inspect KoardErrorType.KoardServiceErrorType on API failures. Methods returning Result can carry the exception as a failure; use getOrThrow() inside your try block to handle it in the same way as thrown exceptions.

HTTP responses are commonly wrapped as HttpError(errorCode), including HTTP 401 and 404. Handle those codes explicitly; do not assume they always become the separate Unauthorized or NotFound objects.

Error Type Description
HttpError(errorCode: Int) Server returned an HTTP error. Inspect errorCode for the status (400, 401, 404, 500, etc.)
InvalidRequest Request validation failed before sending (e.g., negative amount, missing transaction ID)
NotFound Resource not found (404)
Unauthorized Missing or invalid API key / session (401)
UnexpectedError Unexpected server error or empty response body
UnparseableResponse The response body could not be deserialized
ConnectionError Network unreachable, DNS failure, or timeout

DeviceIntegrityError: Security Checks

Thrown when the device fails security validation during enrollment or transaction preparation:

Error Type Reader Code Description
EmulatorDetected 1000 Running on an emulator. Use a physical device
RootDetected 1001 Device is rooted or has superuser binaries
TamperDetected 1002 Device tamper detection triggered
DeveloperModeEnabled 2000 Developer options must be disabled
DebugModeEnabled 2001 USB debugging must be disabled
HookDetected 2003 Runtime instrumentation detected (Frida, Xposed, etc.)
GenericIntegrityFailure -1 Generic fallback when the underlying detail does not identify eligibility or initialization/enrollment

TransactionErrorType: Card & Payment Errors

These public KoardErrorType.TransactionErrorType values classify transaction and validation failures. They have no public numeric-code property. They are distinct from KoardTransactionResponse.statusCode, reader display-message IDs, and HTTP status codes.

Error Type Description
TransactionAmountNonPositive Amount must be greater than zero
RefundMissingParentTransactionId Refund requires a parent transaction ID
CancelOrEnter Cardholder prompted to cancel or confirm
CardError Unrecoverable card data error
NotAuthorisedOrDeclined Issuer declined the transaction
PinRequired PIN entry is required
IncorrectPin Cardholder entered an incorrect PIN
ProcessingError Generic processing failure
TryAnotherCard Card cannot complete. Try a different card
InsertOrSwipe Contactless not supported. Use chip or mag-stripe
TryAnotherChoice Try a different payment method
Cancelled Transaction was cancelled
StrongCvm Strong Customer Verification required (SCA)
PinBypassed PIN entry was bypassed
PinNotProvided PIN was requested but not provided
TransactionNotAllowed Transaction type not allowed on this card/terminal
NotApplicable Status not applicable to this transaction type
UnknownStatus Unmapped status from the reader service
TransactionError Generic transaction error
EnableReader NFC reader needs to be enabled
NetworkError Network error during transaction processing
AuthenticationFailed Authentication with the payment backend failed
CouldNotAttestError Device attestation failed during transaction
TransactionInProgressError Another transaction is already in progress
DeviceDisabledError Device has been disabled for transactions
ErrorLoadingConfig Could not load transaction configuration
TransactionApprovedUploadFailed Approval was reported but upload failed. Reconcile the existing transaction; do not charge again

Reader Error Codes

These tables describe reader diagnostics using numeric codes when available on KoardTransactionResponse.statusCode. A wrapped exception does not necessarily retain a numeric code; handle it through KoardException.error and your fallback path. The category headings below are descriptions, not additional SDK class names.

Reader Communication

Code Meaning and recovery
92 Failed to bind to the reader service
93 Sending a message to the reader service failed
94 Reader response could not be parsed
95 Connector-side serialization failed
96 Companion app missing. Use the SDK installation flow
97 Google Play Protect must be enabled
98 Another merchant app on the device currently holds the lock on the reader service. Close the other app or wait for it to finish. Calling resetKernelService() will not help because the lock is owned by a different process

Reader Environment

Code Meaning and recovery
10 Network unavailable or request timed out
21 Device OS below Android 13
62 Visa Tap to Pay Ready app is outdated

Device Eligibility

Code Meaning and recovery
80 OS build is unsupported
81 No hardware-backed keystore
82 Elliptic-curve crypto unavailable
83 AES crypto unavailable
84 DES crypto unavailable
85 NFC hardware missing or disabled
86 Google Play Services absent or outdated
-1 Underlying detail identifies eligibility failure

Enrollment and Initialization

Code Meaning and recovery
1 Enrollment already exists. Verify the intended merchant/location and readiness before transacting
3 Missing device-auth public key. Re-enroll
4 Enrolled device identifier not provided
52 Random nonce required by enrollment missing
-1 Underlying detail identifies initialization or enrollment failure

Reader Preparation

Code Meaning and recovery
7 Reader authentication failed
11 Reader initialization not completed before use
12 enrollDevice() not completed. Re-enroll
17 Could not load config blobs
18 Reader authentication service error
20 Hardware keystore missing during key prep
24 Device attestation failed
25 Login exchange with payment service failed
26 Login response missing assertion
27 Login response missing crypto payload
28 Entire login response was null
29 Login HTTP body empty
30 Login response missing auth status
31 Shared secret not derived. Re-enroll
32 Session keys missing. Re-enroll
33 MAC/signature mismatch. Possible tampering
34 MAC tag verification failed
36 Payment service rejected authorization
37 Auth status element empty
49 Could not fetch key-rotation seed list
50 TLS pinning failed. Possible MITM
51 Could not derive transaction keys. Re-enroll
87 Reader requested key rotation; this is not a payment approval
88 Key rotation not needed; still check reader readiness
89 Key rotation completed; still check reader readiness
90 Key rotation failed. Re-enroll if transactions fail
91 Reader did not return rotation status

Generic and Unmapped Codes

A code of -1 indicates a generic eligibility, initialization/enrollment, or device-integrity failure. The SDK uses the detail available when wrapping the failure to classify it. Retain the wrapped error and a redacted description; do not assume every -1 has the same cause.

An unrecognized or unavailable underlying code maps to GeneralError. An unrecognized numeric code may appear in shortMessage, but it is not a typed numeric property. Keep a generic recovery path and retain redacted context for support.

Handling Errors in Code

This example enrolls the device without initiating a payment. Call it from your UI scope and supply the showMessage callback. It catches both the SDK invocation and its exceptions, preserves cancellation, and dispatches UI work to Main.

import com.koardlabs.merchant.sdk.KoardMerchantSdk
import com.koardlabs.merchant.sdk.domain.KoardErrorType
import com.koardlabs.merchant.sdk.domain.exception.KoardException
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

suspend fun enrollReader(
    sdk: KoardMerchantSdk,
    showMessage: (String) -> Unit
) {
    try {
        // Authenticate and select an active location before calling this helper.
        val enrollmentMessage = withContext(Dispatchers.IO) {
            sdk.enrollDevice()
        }
        withContext(Dispatchers.Main) { showMessage(enrollmentMessage) }
    } catch (canceled: CancellationException) {
        throw canceled
    } catch (error: KoardException) {
        val message = when (val type = error.error.errorType) {
            is KoardErrorType.KoardServiceErrorType.HttpError -> when (type.errorCode) {
                401, 403 -> "Sign in and verify merchant access."
                429 -> "Too many requests. Wait before trying again."
                else -> "The request failed (HTTP ${type.errorCode})."
            }
            KoardErrorType.KoardServiceErrorType.ConnectionError ->
                "Check connectivity and enrollment state before trying again."
            KoardErrorType.NotReady.NoActiveLocation -> "Select a location first."
            KoardErrorType.NotReady.NotAuthenticated,
            KoardErrorType.KoardServiceErrorType.Unauthorized -> "Sign in first."
            KoardErrorType.NotReady.KernelAppNotInstalled ->
                "Install the Visa Tap to Pay Ready app."
            KoardErrorType.DeviceIntegrityError.DeveloperModeEnabled,
            KoardErrorType.NotReady.DeveloperModeEnabled -> "Disable developer options."
            else -> "Enrollment could not complete. Check setup or contact support."
        }
        // Record the type and a redacted shortMessage in your diagnostic handler.
        withContext(Dispatchers.Main) { showMessage(message) }
    }
}

For Result-returning APIs, put getOrThrow() inside the worker block and the surrounding try, then inspect the returned transaction's operation-specific status. For payments, use the canonical Running Payments implementation rather than adapting this enrollment helper into a payment retry loop. Unexpected exceptions should reach your app's fallback handler.

Troubleshooting and Support

Use Troubleshooting for NFC settings, enrollment, device integrity, and reader service contention. Include the SDK and Android versions, device model, operation, environment, full error type, redacted shortMessage, and transaction/event ID when available. For a reader event, also include statusCode, statusCodeDescription, actionStatus, and finalStatus when present. Omit API keys, credentials, and card data.

Published Sources