commaai/openpilot · error · UnauthorizedError
Unauthorized. Authenticate with openpilot/tools/lib/auth.py
Error message
Unauthorized. Authenticate with openpilot/tools/lib/auth.py
What it means
CommaApi.request() inspects every JSON response: if the body contains an 'error' key and the HTTP status is 401 or 403, it raises UnauthorizedError pointing at the auth helper. The JWT token in the Authorization header is missing, expired, or not accepted for this endpoint. Other error statuses raise generic APIError instead.
Source
Thrown at openpilot/tools/lib/api.py:23
# TODO: this should be merged into common.api
class CommaApi:
def __init__(self, token=None):
self.session = requests.Session()
self.session.headers['User-agent'] = 'OpenpilotTools'
if token:
self.session.headers['Authorization'] = 'JWT ' + token
retries = Retry(total=5, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
self.session.mount('https://', HTTPAdapter(max_retries=retries))
def request(self, method, endpoint, **kwargs):
with self.session.request(method, API_HOST + '/' + endpoint, **kwargs) as resp:
resp_json = resp.json()
if isinstance(resp_json, dict) and resp_json.get('error'):
if resp.status_code in [401, 403]:
raise UnauthorizedError('Unauthorized. Authenticate with openpilot/tools/lib/auth.py')
e = APIError(str(resp.status_code) + ":" + resp_json.get('description', str(resp_json['error'])))
e.status_code = resp.status_code
raise e
return resp_json
def get(self, endpoint, **kwargs):
return self.request('GET', endpoint, **kwargs)
def post(self, endpoint, **kwargs):
return self.request('POST', endpoint, **kwargs)
class APIError(Exception):
pass
class UnauthorizedError(Exception):
pass
View on GitHub (pinned to 516ec1e682)
Solutions
- Authenticate: python openpilot/tools/lib/auth.py (then retry the original tool)
- If already 'logged in', refresh the token by re-running auth.py - expired JWTs are the most common cause
- For CI, provision the token via environment/config so CommaApi picks it up instead of passing none
- Confirm you are accessing your own routes/devices; 403 on foreign resources is expected even with a valid token
Example fix
# before api = CommaApi() # no token -> 401 # after (shell) # python openpilot/tools/lib/auth.py # complete browser login first from openpilot.tools.lib.auth_config import get_token api = CommaApi(token=get_token())
Defensive patterns
Strategy: try-catch
Validate before calling
from openpilot.tools.lib.auth_config import get_token token = get_token() assert token, 'not authenticated - run: python openpilot/tools/lib/auth.py'
Try / catch
from openpilot.tools.lib.api import UnauthorizedError, APIError
try:
data = api.get('v1/route/...')
except UnauthorizedError:
raise SystemExit('token missing/expired - run python tools/lib/auth.py and retry')
except APIError as e:
raise SystemExit(f'api error {getattr(e, "status_code", "?")}: {e}') Prevention
- Check for a stored token before long-running jobs and fail fast instead of mid-run
- Catch UnauthorizedError specifically (it subclasses from APIError family) and prompt re-auth rather than retrying
When it happens
Trigger: Any CommaApi().get()/post() call when ~/.comma/home/... auth token is absent or expired; token valid but the resource belongs to another user's dongle; tools/lib API key not set in the environment.
Common situations: First use of comma tools on a machine without logging in; token older than its expiry after weeks of no use; multiple accounts / CI without credentials.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- error getting route metadata: cannot find any uploaded logs
- no redirect implemented for method {method}
- private key is not configured
- AuthenticateServer rejected by eUICC: {AUTH_SERVER_ERROR_COD
- bundled firmware is {expected_product!r}, expected version {
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/6a87b6b8e545a208.
Report an issue: GitHub.