commaai/openpilot · error · RuntimeError
Invalid BoundProfilePackage
Error message
Invalid BoundProfilePackage
What it means
_split_bpp() chunks the BoundProfilePackage for installation per SGP.22 §5.7.6 by locating the root TAG_BPP (0xBF36) element via iter_tlv with positions. If the decoded bpp bytes contain no BF36 element, the structure is not recognized as a BPP and RuntimeError('Invalid BoundProfilePackage') is raised.
Source
Thrown at openpilot/common/esim/lpa.py:530
return b64e(response)
def _parse_tlv_header_len(data: bytes) -> int:
tag_len = 2 if data[0] & 0x1F == 0x1F else 1
length_byte = data[tag_len]
return tag_len + (1 + (length_byte & 0x7F) if length_byte & 0x80 else 1)
def _split_bpp(bpp: bytes) -> list[bytes]:
"""Split a BoundProfilePackage into APDU chunks per SGP.22 §5.7.6."""
root_value = None
for tag, value, start, end in iter_tlv(bpp, with_positions=True):
if tag == TAG_BPP:
root_value = value
val_start = start + _parse_tlv_header_len(bpp[start:end])
break
if root_value is None:
raise RuntimeError("Invalid BoundProfilePackage")
chunks: list[bytes] = []
for tag, value, start, end in iter_tlv(root_value, with_positions=True):
if tag == TAG_BPP_COMMAND:
chunks.append(bpp[0 : val_start + end])
elif tag in (0xA0, 0xA2):
chunks.append(bpp[val_start + start : val_start + end])
elif tag in (0xA1, 0xA3):
hdr_len = _parse_tlv_header_len(root_value[start:end])
chunks.append(bpp[val_start + start : val_start + start + hdr_len])
for _, _, cs, ce in iter_tlv(value, with_positions=True):
chunks.append(value[cs:ce])
return chunks
def _parse_install_result(response: bytes) -> dict[str, Any] | None:
"""Parse a ProfileInstallResult from an APDU response, or None if not present."""
root = find_tag(response, TAG_PROFILE_INSTALL_RESULT)View on GitHub (pinned to 516ec1e682)
Solutions
- Verify you pass exactly the boundProfilePackage field from the ES9+ handleNotification response to load_bpp
- Inspect the decoded bytes' first tags (hex dump) — a valid BPP starts with BF36; if it starts with another BF tag, the wrong field was used
- Re-run the download from the QR; server-transient truncation yields a new, complete BPP (note: some already-consumed QRs cannot be reused)
Defensive patterns
Strategy: validation
Validate before calling
import base64
from openpilot.common.esim.lpa import find_tag, TAG_BPP
raw = base64.b64decode(b64_bpp)
if find_tag(raw, TAG_BPP) is None:
raise SystemExit('payload is not a BoundProfilePackage (no BF36); wrong field or corrupted BPP') Type guard
def looks_like_bpp(b64: str) -> bool:
try:
return find_tag(base64.b64decode(b64), 0xBF36) is not None
except Exception:
return False Prevention
- Pass exactly the boundProfilePackage field of handleNotification to load_bpp
- Hex-dump the BPP prefix when integration-testing new download flows
- Strip whitespace/newlines from base64 before decoding
When it happens
Trigger: load_bpp() is called with base64 that decodes to bytes lacking a BF36 root — wrong field passed (e.g. profileMetadata instead of boundProfilePackage), truncated BPP from a segmented server response, or a server-format mismatch (SGP.22 version differences in wrapping).
Common situations: Wiring bugs in custom download flows mixing up handleNotification response fields; carrier servers returning the BPP wrapped or encoded differently; base64 corruption (whitespace/newlines) truncating the payload.
Related errors
- Missing {label or f'tag 0x{target:X}'}
- AuthenticateServer rejected by eUICC: {AUTH_SERVER_ERROR_COD
- Profile installation failed: no result from eUICC
- APDU failed with SW={sw1:02X}{sw2:02X}
- Profile nickname must be 64 bytes or less
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/de6bc36f708989d8.
Report an issue: GitHub.