commaai/openpilot · error · RuntimeError
APDU failed with SW={sw1:02X}{sw2:02X}
Error message
APDU failed with SW={sw1:02X}{sw2:02X} What it means
es10x_command() sends APDUs to the eUICC and checks the ISO 7816 status word. SW=61xx means more data (handled via GET RESPONSE), 90xx means success; anything else raises RuntimeError with the two-byte SW in hex. The SW encodes the exact eUICC-side failure per SGP.22 (e.g. 6A88 = referenced data not found, 6985 = conditions of use not satisfied).
Source
Thrown at openpilot/common/esim/lpa.py:371
def es10x_command(client: AtClient, data: bytes) -> bytes:
response = bytearray()
sequence = 0
offset = 0
while offset < len(data):
chunk = data[offset : offset + ES10X_MSS]
offset += len(chunk)
is_last = offset == len(data)
apdu = bytes([0x80, 0xE2, 0x91 if is_last else 0x11, sequence & 0xFF, len(chunk)]) + chunk
segment, sw1, sw2 = client.send_apdu(apdu)
response.extend(segment)
while True:
if sw1 == 0x61: # More data available
segment, sw1, sw2 = client.send_apdu(bytes([0x80, 0xC0, 0x00, 0x00, sw2 or 0]))
response.extend(segment)
continue
if (sw1 & 0xF0) == 0x90:
break
raise RuntimeError(f"APDU failed with SW={sw1:02X}{sw2:02X}")
sequence += 1
return bytes(response)
# --- Profile operations ---
NOTIFICATION: FieldMap = {
TAG_STATUS: ("seqNumber", lambda v: int.from_bytes(v, "big")),
0x81: ("profileManagementOperation",
lambda v: NOTIFICATION_OPERATIONS.get(next((m for m in NOTIFICATION_OPERATIONS if len(v) >= 2 and v[1] & m), 0), "unknown")),
0x0C: ("notificationAddress", lambda v: v.decode("utf-8", errors="ignore")),
TAG_ICCID: ("iccid", tbcd_to_string),
}
def decode_profiles(blob: bytes) -> list[dict]:
root = require_tag(blob, TAG_PROFILE_INFO_LIST, "ProfileInfoList")
list_ok = find_tag(root, TAG_OK)View on GitHub (pinned to 516ec1e682)
Solutions
- Decode SW1SW2: 6A88 referenced data not found, 6A80 incorrect parameters, 6985 use conditions not satisfied (e.g. profile already in target state), 6E00 unsupported instruction
- Re-list profiles (list_profiles) to refresh actual state, then retry against current ICCIDs/states
- If deleting/switching an enabled profile, disable it first when the policy requires disabled-state operations (profileNotInDisabledState behavior)
- On repeated unexpected SWs, reopen the ISD-R channel (channel loss can corrupt APDU framing)
Example fix
# before result = es10x_command(client, encode_tlv(TAG_ENABLE_PROFILE, content)) # RuntimeError: APDU failed with SW=6A88 # after — refresh state and target an existing, correctly-stated profile profiles = list_profiles(client) assert any(p['iccid'] == iccid for p in profiles), 'iccid not on eUICC' # ensure target is disabled before enable per policy result = es10x_command(client, encode_tlv(TAG_ENABLE_PROFILE, content))
Defensive patterns
Strategy: try-catch
Validate before calling
from openpilot.common.esim.lpa import list_profiles
profiles = list_profiles(client)
target = next((p for p in profiles if p['iccid'] == iccid), None)
if target is None:
raise SystemExit('iccid not present; refresh list')
# for enable: ensure profile currently disabled
if action == 'enable' and target['profileState'] == 1:
raise SystemExit('profile already enabled') Try / catch
try:
result = es10x_command(client, cmd_bytes)
except RuntimeError as e:
msg = str(e)
if 'SW=6A88' in msg:
# referenced data not found -> refresh profile state
...
elif 'SW=6985' in msg:
# conditions not satisfied (e.g. wrong profile state) -> adjust and retry
...
raise Prevention
- Refresh list_profiles before state-changing operations and act on live state
- Disable profiles before delete/enable when policy requires disabled state
- Reopen the ISD-R channel after any SW error before the next APDU
When it happens
Trigger: The eUICC refuses an operation: enabling a profile that doesn't exist (6A88), enabling an already-enabled profile (6985 / wrongProfileReenabling), deleting an enabled profile, or protocol errors during a store/load sequence. Any ES10x command whose SW1 high nibble isn't 9 or 6-with-61.
Common situations: Race where profile state changed between list and switch/delete (profile already enabled/deleted); stale channel after modem hiccup causing garbage APDUs; eUICC policy (carrier) disallowing the operation.
Related errors
- SetNickname failed with status 0x{code:02X}
- AuthenticateServer rejected by eUICC: {AUTH_SERVER_ERROR_COD
- Missing {label or f'tag 0x{target:X}'}
- Profile nickname must be 64 bytes or less
- profile {iccid} not found
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/0fd1e048276fb80e.
Report an issue: GitHub.