# LongBridge Token Refresh Workflow ## Problem `LONGBRIDGE_ACCESS_TOKEN` expired/invalid → error: `401004: token invalid` or `401003: token expired`. All LongBridge/LongPort API calls fail simultaneously. ## Fix Steps (Automated — Preferred) 1. Open LongBridge App → 我的 → 设置 → API 密钥管理 → **重新生成** Access Token 2. Copy the new token (starts with `m_`) 3. Run the update script: ```bash bash ~/.hermes/scripts/update_longbridge_token.sh NEW_TOKEN ``` 4. Script auto-updates ALL locations and runs CLI + Python SDK verification ## Token Storage Locations (script updates ALL) | Location | Variables | |----------|-----------| | `~/.bashrc` | `LONGBRIDGE_ACCESS_TOKEN` + `LONGPORT_ACCESS_TOKEN` | | `~/.env` | `LONGBRIDGE_ACCESS_TOKEN` (also `LONGPORT_ACCESS_TOKEN` if exists) | | `~/.hermes/envs/*.env` | Any file containing these vars | ## Manual Fix (if script unavailable) ```bash # 1. Get new token from App # 2. Update bashrc (two lines) sed -i "s|^export LONGBRIDGE_ACCESS_TOKEN=.*|export LONGBRIDGE_ACCESS_TOKEN=NEW_TOKEN|" ~/.bashrc sed -i "s|^export LONGPORT_ACCESS_TOKEN=.*|export LONGPORT_ACCESS_TOKEN=NEW_TOKEN|" ~/.bashrc # 3. Update .env sed -i "s|^LONGBRIDGE_ACCESS_TOKEN=.*|LONGBRIDGE_ACCESS_TOKEN=NEW_TOKEN|" ~/.env # 4. Update hermes envs for f in ~/.hermes/envs/*.env; do [ -f "$f" ] && sed -i "s|^LONGBRIDGE_ACCESS_TOKEN=.*|LONGBRIDGE_ACCESS_TOKEN=NEW_TOKEN|" "$f" done # 5. Verify source ~/.bashrc && source ~/.env && longbridge balance --json ``` ## Token Format JWT mobile session token: `m_..` Prefix `m_` indicates mobile session token (generated from App, not Web console). ## ⚠️ Terminal Masking Trap The terminal tool **masks** secrets in both **output display and environment variables**. ```bash # What you SEE in terminal output: $ grep "ACCESS_TOKEN" ~/.bashrc export LONGBRIDGE_ACCESS_TOKEN=m_eyJh...jb-k # <-- those "..." are MASKING, not real! # What's actually in the file: m_eyJhbGciOiJSUzI1NiIsImtpZCI6ImQ5YWRiMGIxYTdlNzYxNzEi... # (full 1053-char JWT) ``` **Consequences:** - `grep` output showing `...` DOES NOT mean the token is truncated — it means the tool masked it - `source ~/.bashrc && echo $LONGBRIDGE_ACCESS_TOKEN` also shows `...` but the actual env var in the child process may be correct - **NEVER assume `...` in terminal output means the file has placeholders** — always verify via Python `open()` + SHA256 or byte-length check - This masking affects both the `terminal` tool AND the `execute_code` sandbox **How to verify the token is truly intact:** ```bash python3 -c " import hashlib with open('/home/openclaw/.bashrc') as f: for line in f: if 'LONGBRIDGE_ACCESS_TOKEN' in line and 'export' in line: tk = line.strip().split('=', 1)[1] print(f'Token length: {len(tk)}') print(f'SHA256: {hashlib.sha256(tk.encode()).hexdigest()[:16]}') # Length should be ~1053 for a valid JWT " ``` **Key rule:** When the user says "变量没有占位符", they're right — trust them over the masked terminal output. ## JWT Verification (Decode Token) When getting 401004 with what looks like a valid token, decode it to check: ```python import json, base64, time # Strip m_ prefix, decode JWT payload jwt = token[2:] # Remove "m_" payload_b64 = jwt.split('.')[1] # Add padding padding = 4 - len(payload_b64) % 4 if padding != 4: payload_b64 += '=' * padding payload = json.loads(base64.urlsafe_b64decode(payload_b64)) exp = payload['exp'] now = int(time.time()) print(f"Expired: {now > exp}") # Should be False print(f"App Key (ak): {payload['ak']}") # Should match LONGBRIDGE_APP_KEY print(f"EXP: {time.strftime('%Y-%m-%d', time.gmtime(exp))} UTC") ``` **What to check:** | Check | Expected | If wrong | |-------|----------|----------| | `exp > now` | True (not expired) | Token genuinely expired → regenerate | | `ak` matches bashrc | Exact match | Wrong app key → check credentials | | Token length | ~1053 chars | Truncated → re-copy from App | ## 401004 with Fresh Token (Diagnosis) If a **newly-generated** token still gets 401004: 1. **Wait & retry**: Some tokens take 1-2 minutes to propagate. Run `sleep 30 && source ~/.bashrc && longbridge quote --json AAPL.US` 2. **Decode JWT** (see above) to confirm `exp` is in the future and `ak` matches the configured APP_KEY 3. **Re-generate from App**: Occasionally the first generation doesn't register properly. Generate again. 4. **Fallback: Web console**: Go to https://open.longportapp.com/ → Personal Access Token (different from App token, may work when App token doesn't) 5. **Check credentials are intact**: Verify both APP_KEY and APP_SECRET values via Python `open()` + length check (APP_KEY=32 chars, APP_SECRET=64 chars) ## Verification After updating, test with: ```bash source ~/.bashrc && longbridge balance --json ``` Or use the SDK: ```python from longport import openapi cfg = openapi.Config.from_env() ctx = openapi.QuoteContext(config=cfg) print(ctx.quote(['AAPL.US'])[0].last_done) ```