How to Debug APDU Responses — SW1 SW2 Status Word Reference

Every APDU command returns a 2-byte status word (SW1 SW2). 90 00 means success. Everything else is an error or warning. This guide is a ISO 7816-4 status word reference organized by SW1 class, with practical debugging strategies for real-world card interactions: T=0 GET RESPONSE flow, T=1 chaining, security state, and file system errors.

1. SW1 SW2 Encoding

SW1 RangeCategoryMeaning
0x90NormalSuccess; SW2 varies (0x00 = complete, non-zero = proprietary info)
0x61NormalData available via GET RESPONSE; SW2 = byte count
0x62WarningState of non-volatile memory unchanged (card accepted but with caveat)
0x63WarningState of non-volatile memory changed (card accepted but with caveat)
0x64Execution ErrorState of non-volatile memory unchanged (card rejected)
0x65Execution ErrorState of non-volatile memory changed (card rejected)
0x66Security ErrorSecurity-related issue
0x67-0x6BChecking ErrorWrong Lc/Le/P1/P2, or command not supported
0x6CChecking ErrorWrong Le; SW2 = correct Le value
0x6D-0x6FChecking ErrorInstruction not supported / class not supported / no precise diagnosis

2. Most Common Status Words — Quick Reference

SW1 SW2MnemonicMeaningWhat to Do
90 00SUCCESSCommand executed successfullyDone
61 XXDATA_AVAILABLEXX bytes ready for GET RESPONSE (T=0)Send 00 C0 00 00 XX
62 83CHV_BLOCKEDPIN blocked (counter at zero)Use PUK or unblock command
63 CXCHV_WARNINGPIN verify failed; X = remaining attemptsRetry with correct PIN; card locks after 0
67 00WRONG_LENGTHWrong Lc (command data length)Check Lc against file/command spec
69 82SECURITY_NOT_SATISFIEDPIN not verified / access condition not metVerify PIN/authenticate first
69 84DATA_INVALIDATEDData or key referenced is invalidCheck key reference / data object exists
69 85CONDITIONS_NOT_SATISFIEDPre-conditions not metSelect correct DF / authenticate
6A 80WRONG_DATAIncorrect data in command data fieldVerify data format/syntax
6A 82FILE_NOT_FOUNDFile/application not foundCheck FID/AID, select parent DF first
6A 86WRONG_P1P2Incorrect P1 or P2Verify P1P2 against command spec
6A 88REF_DATA_NOT_FOUNDReferenced data (key/PIN) not foundCheck key/PIN reference number
6B 00WRONG_LEWrong Le (expected length)Verify requested length ≤ file size
6C XXWRONG_LE_CORRECTCorrect Le is XXRetry command with Le = XX
6D 00INS_NOT_SUPPORTEDInstruction code not supportedCheck card spec for supported INS values
6E 00CLA_NOT_SUPPORTEDClass byte not supportedCheck card spec for supported CLA values
6F 00NO_PRECISE_DIAGNOSISGeneric error, no further infoRetry with different parameters; check card logs

3. T=0 GET RESPONSE Flow

T=0 protocol is the #1 source of confusion. When the card wants to return data but the command didn't specify Le, or data exceeds the buffer:

# STEP 1: Send command WITHOUT Le
>> 00 A4 04 00 07 A0 00 00 00 03 10 10   # SELECT AID, no Le
<< 61 1A    # "I have 0x1A (26) bytes — ask for them with GET RESPONSE"

# STEP 2: Send GET RESPONSE with correct Le
>> 00 C0 00 00 1A   # GET RESPONSE, Le = 0x1A
<< 6F 15 84 07 ... 90 00   # FCI template data, followed by 90 00

A robust function should handle 61XX and 6CXX automatically:

def send_apdu_robust(connection, apdu):
    """Send APDU, auto-handle T=0 GET RESPONSE and wrong Le."""
    resp, sw1, sw2 = connection.transmit(apdu)

    while True:
        if sw1 == 0x61:
            # Data available: send GET RESPONSE
            get_resp = [0x00, 0xC0, 0x00, 0x00, sw2]
            more, sw1, sw2 = connection.transmit(get_resp)
            resp += more  # Append response data
        elif sw1 == 0x6C:
            # Wrong Le: retry with correct Le from SW2
            apdu[-1] = sw2  # Update Le byte
            resp, sw1, sw2 = connection.transmit(apdu)
        else:
            break
    return resp, sw1, sw2

4. Security State Debugging

Many errors come from incomplete security state. The card maintains a security status (PIN verified, external auth done, secure channel open) that resets on DF change or card reset:

# Common security flow:
# 1. SELECT application (MF or AID)
# 2. VERIFY PIN (if needed) → card now has "PIN verified" state
# 3. EXTERNAL AUTHENTICATE (if GP secure channel) → "auth" state
# 4. Now commands that require security state (6xxx errors) will work

# If you get 6982 (SECURITY_NOT_SATISFIED):
# → You forgot to verify PIN or authenticate first

# If you get 6985 (CONDITIONS_NOT_SATISFIED):
# → Wrong DF selected, or security state was reset by DF selection

5. Debugging Workflow

# 1. Start with a known-good SELECT command:
SELECT_MF = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00]
# Expected: 90 00 (JTAPI/Gemalto cards) or 61 XX + GET RESPONSE

# 2. If 6D 00 → card doesn't support that INS
#    If 6E 00 → card doesn't support that CLA (try 0x80 for GP cards)
#    If 6A 82 → file/AID doesn't exist on this card

# 3. After SELECT, test with a simple READ BINARY:
READ_10 = [0x00, 0xB0, 0x00, 0x00, 0x0A]
# If 69 82 → need PIN/first; if 6A 82 → no EF selected
Test this yourself: Our APDU Response Debugger decodes any SW1 SW2 pair instantly — paste 6982 or 6A82 and get human-readable explanation, common causes, and suggested fixes.

Related Tools

APDU Response Debugger — Decode SW1 SW2 instantly | APDU Command Builder — Build correct commands | APDU Quick Reference — All ISO 7816-4 INS codes | ISO 7816 Protocol Reference