commaai/openpilot · error · RuntimeError
AT command failed: {line}
Error message
AT command failed: {line} What it means
While collecting the response to an AT command, _expect() received a line equal to 'ERROR' or starting with '+CME ERROR' — the modem explicitly rejected the command. The full offending line is embedded in the RuntimeError so the CMS/CME error code is visible.
Source
Thrown at openpilot/common/esim/lpa.py:175
if DEBUG:
print(f"SER >> {cmd}", file=sys.stderr)
self._serial.write((cmd + "\r").encode("ascii"))
def _expect(self) -> list[str]:
lines: list[str] = []
while True:
raw = self._serial.readline()
if not raw:
raise TimeoutError("AT command timed out")
line = raw.decode(errors="ignore").strip()
if not line:
continue
if DEBUG:
print(f"SER << {line}", file=sys.stderr)
if line == "OK":
return lines
if line == "ERROR" or line.startswith("+CME ERROR"):
raise RuntimeError(f"AT command failed: {line}")
lines.append(line)
def _ensure_serial(self, reconnect: bool = False) -> None:
if reconnect:
self.channel = None
try:
if self._serial:
self._serial.close()
except Exception:
pass
self._serial = None
if self._serial is None:
self._serial = Serial(self._device, baudrate=self._baud, timeout=self._timeout)
def query(self, cmd: str) -> list[str]:
self._ensure_serial()
try:
self._send(cmd)View on GitHub (pinned to 516ec1e682)
Solutions
- Read the CME error number in the message and map it (e.g. 10 = SIM not inserted, 13 = SIM failure, 100 = unknown) to the actual condition
- Reset the channel state and reopen: set client.channel = None and call open_isdr() again — the library already does this in send_apdu's retry loop
- If SIM is PIN-locked or missing, resolve that at the modem level (check with AT+CPIN?) before eSIM operations
- Reset the modem via lte.sh and retry once from a clean state if errors persist
Example fix
# before
resp = client.query(f'AT+CGLA={client.channel},{len(hex_payload)},"{hex_payload}"')
# RuntimeError: AT command failed: +CME ERROR: 100
# after — drop the stale channel and reopen the ISD-R before retrying
client.channel = None
client.open_isdr()
resp = client.query(f'AT+CGLA={client.channel},{len(hex_payload)},"{hex_payload}"') Defensive patterns
Strategy: retry
Validate before calling
status = client.query('AT+CPIN?')
if any('SIM not ready' in l or 'ERROR' in l for l in status):
raise SystemExit('SIM/modem not ready; resolve before eSIM ops') Try / catch
try:
client.query('AT+CGLA=...')
except RuntimeError as e:
if 'CME ERROR' in str(e) or str(e).endswith('failed: ERROR'):
client.channel = None
client.open_isdr() # re-establish channel, then retry once
client.query('AT+CGLA=...')
else:
raise Prevention
- Treat any CME error as probable channel loss: clear channel and reopen ISD-R
- Check AT+CPIN? / modem readiness before starting eSIM sessions
- Log the CME code — recurring specific codes point to firmware/SIM issues, not transient ones
When it happens
Trigger: Malformed or unsupported AT commands (wrong parameter count for AT+CGLA, invalid channel/length), modem not registered/not ready for the requested operation, SIM PIN locked, or channel state lost so CGLA references a stale <channel>. '+CME ERROR: <n>' carries the specific mobile-equipment error code.
Common situations: The ISD-R logical channel closed underneath the client (modem reset) so subsequent AT+CGLA=<channel>,... fails; issuing AT commands before the modem finished booting; SIM locked or absent; modem firmware that doesn't support the requested command.
Related errors
- Missing +CGLA response
- AT command timed out
- Failed to open ISD-R after retries
- no eUICC detected
- no profile at index {ref} (have {len(profiles)})
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/dd7b4ee57dde8572.
Report an issue: GitHub.