- Trading skills (OKX, dividend, lottery, quantitative) - Creative skills (ASCII art, diagrams, video) - Development skills (GitHub, debugging, TDD) - Research skills (arXiv, blog monitoring) - Productivity skills (email, documents, notes) - MCP integration skills - Custom user skills
176 lines
6.7 KiB
Markdown
176 lines
6.7 KiB
Markdown
---
|
|
name: hermes-gateway-troubleshooting
|
|
description: >
|
|
Diagnose and fix Hermes Gateway platform adapter connection issues.
|
|
Covers checking gateway state, common adapter errors (is_reconnect,
|
|
credential failures, dependency missing), safe restart procedures,
|
|
and log inspection.
|
|
trigger:
|
|
- gateway not connecting
|
|
- platform disconnected
|
|
- adapter error
|
|
- QQ收不到消息
|
|
- gateway retrying
|
|
- connect() unexpected keyword argument
|
|
- is_reconnect
|
|
- gateway state check
|
|
- 网关连不上
|
|
---
|
|
|
|
# Hermes Gateway Troubleshooting
|
|
|
|
Diagnose why a Hermes Gateway platform adapter won't connect.
|
|
|
|
## 1. Check Gateway State
|
|
|
|
The gateway state file is the single source of truth for platform connection status:
|
|
|
|
```bash
|
|
cat ~/.hermes/gateway_state.json | python3 -m json.tool
|
|
```
|
|
|
|
Look at the `platforms` dict. Each platform has:
|
|
- `state`: `"connected"` ✅, `"disconnected"`, `"retrying"` ❌
|
|
- `error_code`: machine-readable error type or `null`
|
|
- `error_message`: human-readable error or `null`
|
|
|
|
Also check if the gateway process is alive:
|
|
```bash
|
|
ps aux | grep -i gateway | grep -v grep
|
|
systemctl --user is-active hermes-gateway
|
|
```
|
|
|
|
## 2. Categorize the Error
|
|
|
|
### Common Error Patterns
|
|
|
|
| Error message pattern | Likely cause | Fix |
|
|
|-----------------------|-------------|-----|
|
|
| `X.connect() got an unexpected keyword argument 'is_reconnect'` | Adapter `connect()` signature doesn't match base class | Add `*, is_reconnect: bool = False` to adapter's `connect()` |
|
|
| `failed to get access token` / `qq_missing_credentials` | Missing or wrong env vars/config | Check `.env` or `config.yaml` for the platform's API credentials |
|
|
| `aiohttp not installed` / `httpx not installed` | Missing Python dependency | `pip install aiohttp httpx` (or `uv pip install ...`) |
|
|
| `WebSocket closed (code=4914/4915)` | Bot offline or banned | Check bot status on platform's developer portal |
|
|
| `database is locked` (Telethon/SQLite) | Container still running with session DB | Stop container, edit DB, restart |
|
|
| `getUpdates returned 0` (Telegram) | No adapter polling / stale gateway | Kill old gateway process, restart fresh |
|
|
| `Telegram polling conflict` | Two processes sharing same bot token | `systemctl --user stop hermes-gateway`, wait 30s, `start` |
|
|
| `Connect timeout` / `Connection refused` | Outbound connectivity issue | Check proxy/VPN, firewal, or WSS_PROXY setting |
|
|
|
|
## 3. The `is_reconnect` Parameter Mismatch (Common Bug)
|
|
|
|
### Symptom
|
|
|
|
Gateway state shows the platform as `"retrying"` with error:
|
|
```
|
|
XAdapter.connect() got an unexpected keyword argument 'is_reconnect'
|
|
```
|
|
|
|
### Root Cause
|
|
|
|
The base class `BasePlatformAdapter` defines:
|
|
```python
|
|
@abstractmethod
|
|
async def connect(self, *, is_reconnect: bool = False) -> bool:
|
|
```
|
|
|
|
The gateway's reconnect watcher calls `adapter.connect(is_reconnect=True)`. If the adapter's `connect()` doesn't accept the `is_reconnect` keyword-only arg, Python throws `TypeError`.
|
|
|
|
### Fix
|
|
|
|
Add the missing parameter to the adapter's `connect()` method:
|
|
|
|
```python
|
|
# Before (broken):
|
|
async def connect(self) -> bool:
|
|
...
|
|
|
|
# After (fixed):
|
|
async def connect(self, *, is_reconnect: bool = False) -> bool:
|
|
...
|
|
```
|
|
|
|
The adapter doesn't need to *use* `is_reconnect` if it doesn't care about the distinction — just accepting the kwarg is enough to stop the TypeError. For adapters that do care (e.g., Telegram's cold boot drops the stale event queue vs reconnect preserves it), use `is_reconnect` to branch behavior.
|
|
|
|
### Verification
|
|
|
|
After fixing, restart the gateway and check state again:
|
|
|
|
```bash
|
|
systemctl --user stop hermes-gateway
|
|
sleep 30 # critical: let Telegram session expire
|
|
systemctl --user start hermes-gateway
|
|
sleep 10 # wait for startup
|
|
cat ~/.hermes/gateway_state.json | python3 -c "import sys,json; s=json.load(sys.stdin); [print(f' {k}: {v[\"state\"]}') for k,v in s['platforms'].items()]"
|
|
```
|
|
|
|
Also run the adapter's tests:
|
|
```bash
|
|
cd ~/.hermes/hermes-agent
|
|
source ./venv/bin/activate
|
|
python -m pytest tests/gateway/test_<adapter>.py -x -v --tb=short
|
|
```
|
|
|
|
### Pattern: All Adapters Must Match
|
|
|
|
Every Hermes gateway platform adapter must declare `connect()` with `*, is_reconnect: bool = False`. When adding a new adapter or updating an old one, verify this signature matches the base class.
|
|
|
|
## 4. Log Inspection
|
|
|
|
```bash
|
|
# Gateway log (general platform issues)
|
|
tail -100 ~/.hermes/logs/gateway.log | grep -iE "error|fail|disconnect|connect|retry"
|
|
|
|
# Filter by platform tag (e.g., QQBot, Telegram, Weixin)
|
|
tail -100 ~/.hermes/logs/gateway.log | grep -i "QQBot\|Telegram\|Weixin"
|
|
|
|
# Check gateway update timestamps
|
|
grep -E "state.*connected|state.*retrying" ~/.hermes/logs/gateway.log | tail -20
|
|
```
|
|
|
|
## 5. Safe Gateway Restart
|
|
|
|
**Do NOT use `hermes gateway restart`** — it kills the Telegram polling session too quickly. The new adapter starts before the old one's Telegram session expires, causing a permanent polling conflict.
|
|
|
|
### Safe Sequence
|
|
|
|
```bash
|
|
# Step 1: Stop
|
|
systemctl --user stop hermes-gateway
|
|
|
|
# Step 2: Wait for Telegram's session lock to expire (30 seconds minimum)
|
|
sleep 30
|
|
|
|
# Step 3: Start fresh
|
|
systemctl --user start hermes-gateway
|
|
|
|
# Step 4: Wait for startup
|
|
sleep 10
|
|
|
|
# Step 5: Verify
|
|
cat ~/.hermes/gateway_state.json | python3 -m json.tool
|
|
```
|
|
|
|
## 6. Platform-Specific Diagnostics
|
|
|
|
### QQ Bot
|
|
- Check `QQ_APP_ID` and `QQ_CLIENT_SECRET` in `.env`
|
|
- QQ WebSocket needs proxy env var `WSS_PROXY` or `HTTPS_PROXY` (set in `_open_ws`)
|
|
- Error 4914 = bot offline/sandbox (fatal, stop reconnecting)
|
|
- Error 4915 = bot banned (fatal, stop reconnecting)
|
|
- Verify `adapter.py`'s `connect()` accepts `is_reconnect`
|
|
|
|
### Telegram
|
|
- Only one bot token can poll at a time. Stale processes block new sessions.
|
|
- Disable bot privacy mode in @BotFather for group message reading (kick + re-add bot after changing)
|
|
- `free_response_chats` in config must be inside `telegram.extra` (NOT top-level)
|
|
|
|
### Weixin
|
|
- Requires `aiohttp` and `cryptography` packages
|
|
- Check `WX_APP_ID`, `WX_TOKEN`, `WX_AES_KEY` in `.env`
|
|
|
|
## Pitfalls
|
|
|
|
- **🔴 Gateway state file can be stale.** If `gateway_state.json` shows an old error even after restart, the new gateway process may be still starting up. Check `ps aux` for PID changes and wait.
|
|
- **🔴 sleep 30 is not optional** on Telegram restarts. Without it, the new adapter gets 409 Conflict and never connects.
|
|
- **🔴 `is_reconnect` in adapter but not in base class** — the reverse check is also worth making when adding a new adapter: if the adapter uses `is_reconnect` internally but the base class doesn't pass it (hypothetical), it'll silently default to `False` during reconnect. The gateway always passes `is_reconnect=True` on watcher-initiated reconnects.
|
|
- **Test mocks need the same signature.** If a test creates a fake adapter with `async def connect(self):` (no `is_reconnect`), it won't catch the real runtime error.
|