{"record":{"id":"0fd1e048276fb80e","repo":"commaai/openpilot","slug":"apdu-failed-with-sw-sw1-02x-sw2-02x","errorCode":null,"errorMessage":"APDU failed with SW={sw1:02X}{sw2:02X}","messagePattern":"APDU failed with SW=(.+?)(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"openpilot/common/esim/lpa.py","lineNumber":371,"sourceCode":"def es10x_command(client: AtClient, data: bytes) -> bytes:\n  response = bytearray()\n  sequence = 0\n  offset = 0\n  while offset < len(data):\n    chunk = data[offset : offset + ES10X_MSS]\n    offset += len(chunk)\n    is_last = offset == len(data)\n    apdu = bytes([0x80, 0xE2, 0x91 if is_last else 0x11, sequence & 0xFF, len(chunk)]) + chunk\n    segment, sw1, sw2 = client.send_apdu(apdu)\n    response.extend(segment)\n    while True:\n      if sw1 == 0x61:  # More data available\n        segment, sw1, sw2 = client.send_apdu(bytes([0x80, 0xC0, 0x00, 0x00, sw2 or 0]))\n        response.extend(segment)\n        continue\n      if (sw1 & 0xF0) == 0x90:\n        break\n      raise RuntimeError(f\"APDU failed with SW={sw1:02X}{sw2:02X}\")\n    sequence += 1\n  return bytes(response)\n\n\n# --- Profile operations ---\n\nNOTIFICATION: FieldMap = {\n  TAG_STATUS: (\"seqNumber\", lambda v: int.from_bytes(v, \"big\")),\n  0x81: (\"profileManagementOperation\",\n         lambda v: NOTIFICATION_OPERATIONS.get(next((m for m in NOTIFICATION_OPERATIONS if len(v) >= 2 and v[1] & m), 0), \"unknown\")),\n  0x0C: (\"notificationAddress\", lambda v: v.decode(\"utf-8\", errors=\"ignore\")),\n  TAG_ICCID: (\"iccid\", tbcd_to_string),\n}\n\n\ndef decode_profiles(blob: bytes) -> list[dict]:\n  root = require_tag(blob, TAG_PROFILE_INFO_LIST, \"ProfileInfoList\")\n  list_ok = find_tag(root, TAG_OK)","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/common/esim/lpa.py#L353-L389","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"# before\nresult = es10x_command(client, encode_tlv(TAG_ENABLE_PROFILE, content))\n# RuntimeError: APDU failed with SW=6A88\n\n# after — refresh state and target an existing, correctly-stated profile\nprofiles = list_profiles(client)\nassert any(p['iccid'] == iccid for p in profiles), 'iccid not on eUICC'\n# ensure target is disabled before enable per policy\nresult = es10x_command(client, encode_tlv(TAG_ENABLE_PROFILE, content))","handlingStrategy":"try-catch","validationCode":"from openpilot.common.esim.lpa import list_profiles\n\nprofiles = list_profiles(client)\ntarget = next((p for p in profiles if p['iccid'] == iccid), None)\nif target is None:\n  raise SystemExit('iccid not present; refresh list')\n# for enable: ensure profile currently disabled\nif action == 'enable' and target['profileState'] == 1:\n  raise SystemExit('profile already enabled')","typeGuard":null,"tryCatchPattern":"try:\n  result = es10x_command(client, cmd_bytes)\nexcept RuntimeError as e:\n  msg = str(e)\n  if 'SW=6A88' in msg:\n    # referenced data not found -> refresh profile state\n    ...\n  elif 'SW=6985' in msg:\n    # conditions not satisfied (e.g. wrong profile state) -> adjust and retry\n    ...\n  raise","preventionTips":["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"],"tags":["esim","apdu","sgp22","euicc","python"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}