{"record":{"id":"62425b53ffc4d9a4","repo":"ZhuLinsen/daily_stock_analysis","slug":"oauth-token-longbrid","errorCode":null,"errorMessage":"OAuth token 缓存已失效或缺失，当前为无头运行不支持打开授权页面，请重建 LONGBRIDGE_OAUTH_TOKEN_CACHE_B64: {url}","messagePattern":"OAuth token 缓存已失效或缺失，当前为无头运行不支持打开授权页面，请重建 LONGBRIDGE_OAUTH_TOKEN_CACHE_B64: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"data_provider/longbridge_fetcher.py","lineNumber":313,"sourceCode":"    except UnicodeDecodeError as exc:\n        logger.warning(\"[Longbridge] OAuth token 缓存不是 UTF-8 文本: %s\", exc)\n        return False\n\n    try:\n        data = json.loads(payload)\n    except json.JSONDecodeError as exc:\n        logger.warning(\"[Longbridge] OAuth token 缓存不是合法 JSON: %s\", exc)\n        return False\n\n    if not isinstance(data, dict) or not data:\n        logger.warning(\"[Longbridge] OAuth token 缓存内容为空或格式不符合预期: %s\", token_cache)\n        return False\n\n    return True\n\n\ndef _oauth_reauth_not_supported(url: str) -> None:\n    raise RuntimeError(\n        f\"OAuth token 缓存已失效或缺失，当前为无头运行不支持打开授权页面，请重建 LONGBRIDGE_OAUTH_TOKEN_CACHE_B64: {url}\"\n    )\n\n\ndef _oauth_sdk_unavailable_error() -> RuntimeError:\n    return RuntimeError(\n        \"当前安装的 longbridge SDK 不支持 OAuth 2.0（缺少 OAuthBuilder/Config.from_oauth）。\"\n        \"请在支持该 SDK 版本的平台安装 longbridge>=4.0.0，或继续使用 Legacy 三件套。\"\n    )\n\n\ndef _longbridge_credentials(config: Any = None) -> Dict[str, Optional[str]]:\n    \"\"\"Collect Longbridge auth inputs from Config/env without exposing secrets.\"\"\"\n    app_key = _clean_optional(getattr(config, \"longbridge_app_key\", None))\n    app_secret = _clean_optional(getattr(config, \"longbridge_app_secret\", None))\n    access_token = _clean_optional(getattr(config, \"longbridge_access_token\", None))\n    oauth_client_id = _clean_optional(getattr(config, \"longbridge_oauth_client_id\", None))\n","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/data_provider/longbridge_fetcher.py#L295-L331","documentation":"RuntimeError from the Longbridge OAuth path: the LONGBRIDGE_OAUTH_TOKEN_CACHE_B64 cache is missing, expired, or malformed, and re-authorization requires opening a browser page — impossible in the current headless run. The URL in the message is the auth page that cannot be opened; the cache must be rebuilt on a machine with a browser.","triggerScenarios":"Running LongbridgeFetcher on a server/CI/container where the cached OAuth token (env var LONGBRIDGE_OAUTH_TOKEN_CACHE_B64) is absent or has expired (refresh token invalid), and _oauth_reauth_not_supported fires instead of launching the OAuth flow.","commonSituations":"Deploying to Docker/GitHub Actions with stale or forgotten token env; token expired since last interactive login; JSON corrupted (the code also warns on JSONDecodeError / empty dict just above); SDK >=4.0.0 with OAuthBuilder on a headless host.","solutions":["On a desktop with a browser, run the interactive Longbridge OAuth flow to mint a fresh token cache, base64-encode it, and set LONGBRIDGE_OAUTH_TOKEN_CACHE_B64 in the headless environment.","Validate the cache before deploying: it must be valid JSON, a non-empty dict (the _validate logic above checks this).","Alternative: fall back to the Legacy credential triple (app key/secret/access token) if OAuth is not required, or install a longbridge version matching the supported path."],"exampleFix":"# headless machine\nexport LONGBRIDGE_OAUTH_TOKEN_CACHE_B64=$(base64 -w0 token_cache.json)","handlingStrategy":"validation","validationCode":"import base64, json, os\ncache_b64 = os.getenv('LONGBRIDGE_OAUTH_TOKEN_CACHE_B64', '')\ntry:\n    data = json.loads(base64.b64decode(cache_b64)) if cache_b64 else None\n    token_ok = isinstance(data, dict) and bool(data)\nexcept Exception:\n    token_ok = False\nif not token_ok:\n    logger.error('Longbridge OAuth cache invalid — regenerate on a desktop host')","typeGuard":"def longbridge_oauth_cache_valid() -> bool:\n    b64 = os.getenv('LONGBRIDGE_OAUTH_TOKEN_CACHE_B64', '')\n    try:\n        d = json.loads(base64.b64decode(b64)) if b64 else None\n        return isinstance(d, dict) and bool(d)\n    except Exception:\n        return False","tryCatchPattern":"try:\n    df = longbridge.get_daily_data(code, ...)\nexcept RuntimeError as e:\n    if 'LONGBRIDGE_OAUTH_TOKEN_CACHE_B64' in str(e):\n        disable_source('longbridge')  # headless re-auth impossible; use legacy creds or skip","preventionTips":["Regenerate the OAuth cache interactively before it expires; calendar a refresh.","Validate the base64+JSON cache in deployment preflight checks.","Keep Legacy app-key/secret/access-token credentials as an operational fallback."],"tags":["longbridge","oauth","headless","configuration","auth"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}