commaai/openpilot · error · LPAError
refusing to delete a comma profile
Error message
refusing to delete a comma profile
What it means
delete_profile refuses with LPAError when the target profile has is_comma set — a safety interlock preventing deletion of the device's own comma-managed operational profile. Deleting it could break the device's cellular connectivity managed by comma.
Source
Thrown at openpilot/common/esim/lpa.py:742
)
for p in list_profiles(self._client)
]
def get_active_profile(self) -> Profile | None:
return None
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:View on GitHub (pinned to 516ec1e682)
Solutions
- Skip is_comma profiles when iterating list_profiles for deletion
- Delete only user-installed profiles identified by ICCID
- If intentional decommissioning is needed, use comma's official deprovisioning path rather than delete_profile
Example fix
// before
for p in lpa.list_profiles():
lpa.delete_profile(p.iccid)
// after
for p in lpa.list_profiles():
if not p.is_comma:
lpa.delete_profile(p.iccid) Defensive patterns
Strategy: type-guard
Validate before calling
target = next((p for p in lpa.list_profiles() if p.iccid == iccid), None)
if target is None or target.is_comma:
skip_delete(iccid) Type guard
def is_deletable(profile) -> bool:
return not profile.is_comma Try / catch
try:
lpa.delete_profile(iccid)
except LPAError as e:
if "comma profile" in str(e):
log.warning("refusing to delete comma profile")
else:
raise Prevention
- Filter is_comma profiles out of any bulk-delete loop
- Surface the interlock in UI so users understand the restriction
When it happens
Trigger: Calling delete_profile on the profile whose metadata marks it as the comma provisioning profile (profile.is_comma True).
Common situations: User or tool attempts to wipe all profiles including the device's own; automated cleanup loops that iterate every profile from list_profiles().
Related errors
- Profile installation failed at {cmd_name}: {err_name}
- DeleteProfile failed: {PROFILE_ERROR_CODES.get(code, 'unknow
- EnableProfile failed: {PROFILE_ERROR_CODES.get(code, 'unknow
- APDU failed with SW={sw1:02X}{sw2:02X}
- SetNickname failed with status 0x{code:02X}
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/1f9abc8e41bc7e9a.
Report an issue: GitHub.