# QQ WebSocket Timeout — Session-Specific Details

## Error Pattern (2026-05-19)

```
WARNING gateway.platforms.qqbot.adapter: [QQBot:1903996332] WebSocket closed: code=4009 reason=Session timed out
```

Followed by gateway crash loop:
```
ERROR gateway.run: kanban dispatcher: tick failed on board default
sqlite3.OperationalError: no such column: session_id
```

And:
```
WARNING gateway.run: SQLite session store not available, falling back to JSONL: file is not a database
```

## Root Cause Chain

1. QQ WebSocket timed out (code=4009) — normal network hiccup
2. Gateway tried to restart but kanban.db had schema from old version (missing `session_id` column)
3. state.db was also corrupted (header bytes wrong — `SQLit\x17` instead of `SQLite format 3`)
4. Gateway entered crash-restart loop (systemd auto-restart)

## Exact Fix Applied

```bash
# Step 1: Move corrupt state.db
mv /root/.hermes/state.db /root/.hermes/state.db.corrupt

# Step 2: Add missing column to kanban.db
python3 -c "
import sqlite3
conn = sqlite3.connect('/root/.hermes/kanban.db')
conn.execute('ALTER TABLE tasks ADD COLUMN session_id TEXT')
conn.commit()
conn.close()
"

# Step 3: Restart gateway
sudo systemctl restart hermes-gateway
```

## Key Lessons

- **state.db corruption**: File header was wrong. Gateway gracefully falls back to JSONL. Safe to move/delete.
- **kanban.db schema drift**: Hermes version updates may add new columns. The error `no such column: session_id` means the code expects a column that doesn't exist yet. Fix: `ALTER TABLE tasks ADD COLUMN <missing_column>`.
- **Gateway crash loop**: systemd keeps restarting, but each start fails immediately. Check `journalctl -u hermes-gateway` for the real error — the crash message appears within seconds of startup.
- **Approval required**: `sudo systemctl restart hermes-gateway` requires user approval. Coordinate with user: "I'll restart, you approve."

## Database File Locations

| File | Purpose | Safe to Delete? |
|------|---------|-----------------|
| `/root/.hermes/state.db` | Session store | Yes (rebuilt from JSONL) |
| `/root/.hermes/kanban.db` | Kanban tasks | No (add missing columns instead) |
| `/root/.hermes/response_store.db` | Response cache | Yes (will rebuild) |

## Checking Database Integrity

```bash
# Check if a file is a valid SQLite database
python3 -c "
import sqlite3
for db in ['state.db', 'kanban.db', 'response_store.db']:
    try:
        conn = sqlite3.connect(f'/root/.hermes/{db}')
        conn.execute('PRAGMA integrity_check')
        print(f'{db}: OK')
    except Exception as e:
        print(f'{db}: ERROR - {e}')
"
```

## Checking File Header

```bash
# SQLite files should start with "SQLite format 3\0"
python3 -c "
with open('/root/.hermes/state.db', 'rb') as f:
    header = f.read(16)
    print('Header:', header)
    print('Valid:', header[:6] == b'SQLite')
"
```
