commaai/openpilot · error · LPAError

DeleteProfile failed: {PROFILE_ERROR_CODES.get(code, 'unknow

Error message

DeleteProfile failed: {PROFILE_ERROR_CODES.get(code, 'unknown')} (0x{code:02X})

What it means

Raised when the eUICC's DeleteProfile response carries a status code other than PROFILE_OK. The hex status and its name from PROFILE_ERROR_CODES are included (e.g. ICCID not found, profile enabled, eUICC memory error).

Source

Thrown at openpilot/common/esim/lpa.py:748

  def process_notifications(self) -> None:
    if not system_time_valid():
      raise RuntimeError("System time is not set; TLS certificate validation requires a valid clock")
    with self._acquire_channel():
      process_notifications(self._client)

  def delete_profile(self, iccid: str) -> None:
    profile = next((p for p in self.list_profiles() if p.iccid == iccid), None)
    if profile is None:
      raise LPAProfileNotFoundError(f"profile not found: {iccid}")
    if profile.is_comma:
      raise LPAError("refusing to delete a comma profile")
    with self._acquire_channel():
      request = encode_tlv(TAG_DELETE_PROFILE, encode_tlv(TAG_ICCID, string_to_tbcd(iccid)))
      response = es10x_command(self._client, request)
      code = require_tag(require_tag(response, TAG_DELETE_PROFILE, "DeleteProfileResponse"), TAG_STATUS, "DeleteProfile status")[0]
    if code != PROFILE_OK:
      raise LPAError(f"DeleteProfile failed: {PROFILE_ERROR_CODES.get(code, 'unknown')} (0x{code:02X})")

  def download_profile(self, qr: str, nickname: str | None = None) -> None:
    with self._acquire_channel():
      iccid = download_profile(self._client, qr)
      if nickname and iccid:
        set_profile_nickname(self._client, iccid, nickname)

  def nickname_profile(self, iccid: str, nickname: str) -> None:
    with self._acquire_channel():
      set_profile_nickname(self._client, iccid, nickname)

  def _enable_profile(self, iccid: str) -> int:
    inner = encode_tlv(TAG_OK, encode_tlv(TAG_ICCID, string_to_tbcd(iccid)))
    inner += b'\x01\x01\x01'  # refreshFlag=1
    response = es10x_command(self._client, encode_tlv(TAG_ENABLE_PROFILE, inner))
    return require_tag(require_tag(response, TAG_ENABLE_PROFILE, "EnableProfileResponse"), TAG_STATUS, "EnableProfile status")[0]

  def switch_profile(self, iccid: str) -> None:

View on GitHub (pinned to 516ec1e682)

Solutions

  1. Disable/switch away from the profile before deleting it
  2. Retry with the exact ICCID from a fresh list_profiles()
  3. Process pending notifications, then retry — install notifications can block deletes on some eUICCs
Defensive patterns

Strategy: retry

Validate before calling

profile = next((p for p in lpa.list_profiles() if p.iccid == iccid), None)
if profile is not None and profile.enabled:
    switch_or_disable_first()  # eUICCs often refuse deleting enabled profiles

Try / catch

try:
    lpa.delete_profile(iccid)
except LPAError as e:
    if "DeleteProfile failed" in str(e):
        lpa.process_notifications()
        lpa.delete_profile(iccid)  # one retry after clearing notifications

Prevention

When it happens

Trigger: Calling delete_profile on an enabled profile (many eUICCs require disable first), an ICCID unknown to the eUICC, or while another profile operation holds the eUICC.

Common situations: Deleting the currently active/enabled profile; concurrent profile operations; stale ICCID after a profile switch.

Related errors


AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15). Data as JSON: /api/errors/e5d0ebf724ff4227. Report an issue: GitHub.