{"record":{"id":"dd8cdc548a21f1a3","repo":"calesthio/OpenMontage","slug":"failed-to-load-refresh-service-account-credentials","errorCode":null,"errorMessage":"Failed to load/refresh service-account credentials from {path}: {exc}","messagePattern":"Failed to load/refresh service-account credentials from (.+?): (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"tools/google_credentials.py","lineNumber":129,"sourceCode":"        raise RuntimeError(\n            \"Service-account auth requires the 'google-auth' package. \"\n            \"Install it with: pip install google-auth\"\n        ) from exc\n\n    path = os.environ.get(\"GOOGLE_APPLICATION_CREDENTIALS\")\n    if not path or not os.path.exists(path):\n        raise RuntimeError(\n            \"GOOGLE_APPLICATION_CREDENTIALS is not set or points to a missing \"\n            \"file; cannot use service-account authentication.\"\n        )\n\n    try:\n        creds = service_account.Credentials.from_service_account_file(\n            path, scopes=scopes\n        )\n        creds.refresh(Request())\n    except Exception as exc:  # noqa: BLE001 - re-raised as actionable message\n        raise RuntimeError(\n            f\"Failed to load/refresh service-account credentials from {path}: {exc}\"\n        ) from exc\n\n    token = creds.token\n    if not token or not isinstance(token, str):\n        raise RuntimeError(\n            \"Service-account credentials did not yield a valid access token.\"\n        )\n\n    project_id = getattr(creds, \"project_id\", None)\n    ret_project_id = str(project_id) if project_id is not None else None\n    return token, ret_project_id\n","sourceCodeStart":111,"sourceCodeEnd":142,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/google_credentials.py#L111-L142","documentation":"RuntimeError wrapping any exception raised while loading the JSON key (service_account.Credentials.from_service_account_file) or refreshing it (creds.refresh(Request())). The chained original exception (`from exc`) carries the true cause: malformed JSON, missing scopes on the key, clock skew, revoked key, or network failure reaching Google's token endpoint.","triggerScenarios":"GOOGLE_APPLICATION_CREDENTIALS points to a file that is not a valid service-account JSON (human-readable key export, YAML, truncated download), the key's project/service account is disabled or deleted, or the refresh HTTP request fails (offline, proxy, DNS, SSL inspection).","commonSituations":"Pasting a Firebase/console key in the wrong format, expired or rotated keys still referenced, corporate proxies intercepting oauth2.googleapis.com, or system clock drift invalidating JWT assertions.","solutions":["Read the tail of the chained exception text — it distinguishes JSON parse errors from network/401 failures.","Validate the key file: it must be a service-account JSON with client_email and private_key fields: `python -c \"import json;k=json.load(open(p));print(k['client_email'])\"`.","If refresh got 401/400 invalid_grant, re-download a fresh key from the console and update the env var; check the VM/service account is enabled.","If it is a network error, verify outbound access to oauth2.googleapis.com:443 (proxy/cert settings)."],"exampleFix":"# before\nos.environ[\"GOOGLE_APPLICATION_CREDENTIALS\"] = \"key.yaml\"  # wrong format -> RuntimeError('Failed to load/refresh ...')\n\n# after\nimport json, os\nkey_path = \"service-account.json\"\njson.load(open(key_path))[\"client_email\"]  # fast structural validation\nos.environ[\"GOOGLE_APPLICATION_CREDENTIALS\"] = key_path","handlingStrategy":"try-catch","validationCode":"import json, os\nfrom pathlib import Path\n\ndef key_file_plausible() -> bool:\n    p = Path(os.environ.get(\"GOOGLE_APPLICATION_CREDENTIALS\", \"\"))\n    if not p.is_file():\n        return False\n    try:\n        data = json.loads(p.read_text())\n    except json.JSONDecodeError:\n        return False\n    return \"client_email\" in data and \"private_key\" in data","typeGuard":null,"tryCatchPattern":"try:\n    token, project = get_service_account_token()\nexcept RuntimeError as e:\n    cause = e.__cause__\n    if cause and \"invalid_grant\" in str(cause):\n        rotate_key_file()  # 401/invalid_grant -> refresh the key, then retry\n    elif cause and isinstance(cause, OSError):\n        check_network_egress(\"oauth2.googleapis.com\")\n    raise","preventionTips":["Structurally validate the key JSON (client_email/private_key) at startup.","Treat invalid_grant as a key-rotation event, not a code bug.","Verify outbound HTTPS to oauth2.googleapis.com in proxied environments."],"tags":["google","authentication","credentials","network","service-account"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}