commaai/openpilot · error · RuntimeError
Confirmation code required but not provided
Error message
Confirmation code required but not provided
What it means
prepare_download() reads ccRequiredFlag from the server's smdpSigned2. When the flag is nonzero the SM-DP+ demands a confirmation code (the activation code's part after the '$$' in the QR, or one issued by the carrier). If the caller passed cc=None or an empty string in that case, RuntimeError is raised before any eUICC interaction.
Source
Thrown at openpilot/common/esim/lpa.py:507
raise RuntimeError(f"AuthenticateServer rejected by eUICC: {AUTH_SERVER_ERROR_CODES.get(code, 'unknown')} (0x{code:02X})")
return b64e(response)
def prepare_download(client: AtClient, b64_signed2: str, b64_sig2: str, b64_cert: str, cc: str | None = None) -> str:
smdp_signed2 = b64d(b64_signed2)
smdp_signature2 = b64d(b64_sig2)
smdp_certificate = b64d(b64_cert)
smdp_signed2_root = find_tag(smdp_signed2, 0x30)
if smdp_signed2_root is None:
raise RuntimeError("Invalid smdpSigned2")
transaction_id = find_tag(smdp_signed2_root, TAG_STATUS)
cc_required_flag = find_tag(smdp_signed2_root, 0x01)
if transaction_id is None or cc_required_flag is None:
raise RuntimeError("Invalid smdpSigned2")
content = smdp_signed2 + smdp_signature2
if int.from_bytes(cc_required_flag, "big") != 0:
if not cc:
raise RuntimeError("Confirmation code required but not provided")
content += encode_tlv(0x04, hashlib.sha256(hashlib.sha256(cc.encode("utf-8")).digest() + transaction_id).digest())
content += smdp_certificate
response = es10x_command(client, encode_tlv(TAG_PREPARE_DOWNLOAD, content))
require_tag(response, TAG_PREPARE_DOWNLOAD, "PrepareDownloadResponse")
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:View on GitHub (pinned to 516ec1e682)
Solutions
- Extract the confirmation code from the activation string: everything after the first '$$' in the QR code payload
- Prompt the user for the CC when the carrier provided one separately (SMS/email)
- Retry download passing the CC: download flow with cc='CODE' (via the CLI: append $$CODE in the QR argument if supported, or the API's cc parameter)
Example fix
# before
prepare_download(client, b64_signed2, b64_sig2, b64_cert) # cc=None
# RuntimeError: Confirmation code required but not provided
# after — parse CC from the QR activation string
qr = 'LPA:1$rsp.truphone.com$QRF-TEST$$4321'
cc = qr.split('$$', 1)[1] if '$$' in qr else None
prepare_download(client, b64_signed2, b64_sig2, b64_cert, cc=cc) Defensive patterns
Strategy: validation
Validate before calling
def extract_confirmation_code(qr: str) -> str | None:
return qr.split('$$', 1)[1] if '$$' in qr else None
cc = extract_confirmation_code(qr_string)
# pass cc through to the download flow; server will still verify it Try / catch
try:
prepare_download(client, s2, sig2, cert, cc=cc)
except RuntimeError as e:
if 'Confirmation code required' in str(e):
cc = input('carrier confirmation code: ').strip()
prepare_download(client, s2, sig2, cert, cc=cc)
else:
raise Prevention
- Parse the full QR activation string including the $$CC suffix at download start
- Prompt interactively for CC whenever the carrier plan is known to require one
- Don't strip '$$' segments during QR sanitization
When it happens
Trigger: Downloading a profile whose QR/activation code includes a confirmation code (format ...$QRF-XXX$$CC123) but calling the download flow without extracting and passing the CC; carriers that always require a CC (e.g. some enterprise plans).
Common situations: QR string of the form LPA:1$rsp.example.com$ACTIVATION$$CODE where the code after $$ was stripped by the parser; interactive flows that didn't prompt; automation that ignores CC.
Related errors
- Profile nickname must be 64 bytes or less
- AuthenticateServer rejected by eUICC: {AUTH_SERVER_ERROR_COD
- Invalid BoundProfilePackage
- Profile installation failed: no result from eUICC
- no profile at index {ref} (have {len(profiles)})
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/c5941d9e2af595c8.
Report an issue: GitHub.