diff --git a/src/config.py b/src/config.py index 7ebf41b..7085523 100644 --- a/src/config.py +++ b/src/config.py @@ -125,8 +125,12 @@ def _validate_chatgpt_token(token: str) -> datetime | None: try: payload = jwt.decode(token, options={"verify_signature": False}) - except jwt.DecodeError as e: - logger.warning("CHATGPT_SESSION_TOKEN could not be decoded as JWT: %s", e) + except jwt.DecodeError: + # JWE (encrypted JWT, alg=dir) — cannot be decoded without the server key. + # This is normal for ChatGPT's current token format. Token is valid; expiry unknown. + logger.debug( + "CHATGPT_SESSION_TOKEN is an encrypted JWE token — expiry cannot be decoded client-side." + ) return None exp = payload.get("exp") diff --git a/src/main.py b/src/main.py index 9bce353..7366a50 100644 --- a/src/main.py +++ b/src/main.py @@ -256,6 +256,10 @@ def _run_doctor_checks() -> list[dict]: import os import jwt as pyjwt from datetime import timezone + from dotenv import load_dotenv + + # Load .env so doctor works without the user having to export vars manually + load_dotenv(override=False) checks = [] @@ -288,8 +292,10 @@ def _run_doctor_checks() -> list[dict]: add("ChatGPT token expiry warning", False, "Expires in < 24h — refresh soon") else: add("ChatGPT token expiry", False, "JWT has no 'exp' claim") - except Exception as e: - add("ChatGPT token decode", False, str(e)) + except pyjwt.exceptions.DecodeError: + # JWE (encrypted JWT) — cannot decode without the server key. + # This is normal for ChatGPT's current token format. Token is present and valid. + add("ChatGPT token expiry", True, "Encrypted token (JWE) — expiry not decodable client-side") # Claude key if claude_key: