# How to Troubleshoot Common Authentication Token Issues in AI‑Trader: 7 Diagnostic Methods

> Troubleshoot AI-Trader authentication token issues with 7 diagnostic methods. Learn to fix malformed headers expired sessions and stale agent tokens for seamless AI trading.

- Repository: [✨Data Intelligence Lab@HKU✨/AI-Trader](https://github.com/HKUDS/AI-Trader)
- Tags: how-to-guide
- Published: 2026-05-09

---

**Most AI‑Trader authentication failures stem from malformed `Authorization` headers, expired user sessions, or stale agent tokens after rotation, all of which can be diagnosed by inspecting the header format and the `agents` or `user_tokens` database tables.**

AI‑Trader (HKUDS/AI-Trader) implements a dual-token architecture that isolates trading-bot credentials from human user sessions. Understanding how to troubleshoot common authentication token issues in AI‑Trader requires familiarity with both the **agent token** system (for automated trading bots) and the **user session token** system (for UI and API clients), each managed in [`service/server/services.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/services.py) and extracted via [`service/server/utils.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/utils.py).

## 1. Verify the Authorization Header Format

Missing or malformed headers generate immediate `401 – Invalid token` errors because `utils._extract_token` expects a specific Bearer format.

In [`service/server/utils.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/utils.py) (lines 27–33), the extraction logic strips the `Bearer ` prefix only if present:

```python
def _extract_token(authorization: str = None) -> Optional[str]:
    if not authorization:
        return None
    if authorization.startswith("Bearer "):
        return authorization[7:]
    return authorization

```

**Symptoms:** Every authenticated endpoint returns `detail: 'Invalid token'`.

**Diagnostic check:**

```bash
curl -v http://localhost:8000/api/users/me   # No Authorization header

```

**Fix:** Ensure the client sends `Authorization: Bearer <token>`. While `utils._extract_token` falls back to returning the raw string if the prefix is missing, relying on this creates inconsistency with external API clients that enforce the Bearer scheme.

## 2. Handle Stale Agent Tokens After Rotation

Agent tokens are generated using `secrets.token_urlsafe(32)` inside `services._issue_agent_token` (lines 54–62 in [`service/server/services.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/services.py)). When rotation occurs, the old value in the `agents` table is overwritten, invalidating previous requests.

**Symptoms:** `401 – Invalid token` immediately after calling a rotation endpoint.

**Diagnostic check:**

```sql
SELECT id, name, token FROM agents WHERE id = <agent_id>;

```

**Fix:** Update the client configuration with the new token returned by the rotation response. The server stores the new credential in the `token` column and clears `token_expires_at` on rotation.

## 3. Diagnose Expired User Session Tokens

User sessions default to a **7‑day TTL** defined in `services._create_user_session` (lines 82–90). Tokens are validated via `services._get_user_by_token` (lines 65–73), which queries the `user_tokens` table.

**Symptoms:** Authentication works intermittently or fails after a week.

**Diagnostic check:**

```sql
SELECT token, expires_at FROM user_tokens WHERE token = '<token>';

```

**Fix:** If `expires_at` is less than the current UTC timestamp, re-authenticate via `/api/users/login` to obtain a fresh token. Alternatively, modify the `timedelta(days=7)` value in `services._create_user_session` to extend the TTL.

## 4. Prevent Accidental Token Deletion During Cleanup

The periodic task `utils.cleanup_expired_tokens` (lines 93–105 in [`service/server/utils.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/utils.py)) deletes rows from `user_tokens` where `expires_at` exceeds the current time.

```python
def cleanup_expired_tokens():
    now = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
    cursor.execute("DELETE FROM user_tokens WHERE expires_at < ?", (now,))

```

**Symptoms:** Tokens vanish from the database unexpectedly, causing sporadic `401` errors.

**Fix:** Ensure `expires_at` is stored in UTC ISO‑8601 format (e.g., `2024-01-15T10:00:00Z`). If using custom TTL values, verify they exceed the intended session duration to prevent the cleanup routine from removing active sessions.

## 5. Fix Wallet‑Based Agent Token Recovery Failures

The recovery flow at `/api/agents/token-recovery` relies on `utils.validate_address` (lines 12–24 in [`service/server/utils.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/utils.py)), which normalizes Ethereum addresses and returns an empty string on validation failure.

**Symptoms:** `400 – Agent has no wallet‑based recovery configured`.

**Diagnostic check:** Verify the agent’s `wallet_address` column contains a valid 40‑character hexadecimal address (e.g., `0x1234...abcd`) stripped of `0x` and lowercased by the validation logic.

**Fix:** Update the `wallet_address` field in the `agents` table to a valid Ethereum address before attempting recovery.

## 6. Standardize Bearer Prefix Handling

Both [`routes_agent.py`](https://github.com/HKUDS/AI-Trader/blob/main/routes_agent.py) and [`routes_users.py`](https://github.com/HKUDS/AI-Trader/blob/main/routes_users.py) (e.g., lines 84–86) invoke `utils._extract_token`. While the function tolerates raw tokens, mixing prefixed and unprefixed headers across clients creates debugging complexity.

**Symptoms:** Authentication succeeds in some clients but fails in others (like Postman or cURL) despite using the same token value.

**Fix:** Enforce `Authorization: Bearer <token>` uniformly across all clients. Consider modifying `utils._extract_token` to reject unprefixed values if strict compliance is required.

## 7. Resolve Database Lock Errors During Token Issuance

SQLite’s write locks can trigger `sqlite3.OperationalError: database is locked` during concurrent token creation. While `services._add_agent_points` implements retry logic, token issuance functions do not currently retry.

**Symptoms:** Intermittent failures when calling login or rotation endpoints under load.

**Diagnostic check:** Monitor the application logs for lock errors coinciding with high write volume.

**Fix:** Reduce concurrent write contention or migrate to PostgreSQL using [`scripts/migrate_sqlite_to_postgres.py`](https://github.com/HKUDS/AI-Trader/blob/main/scripts/migrate_sqlite_to_postgres.py) to eliminate SQLite’s locking limitations.

## Summary

- **AI‑Trader uses two distinct tokens:** Agent tokens (in the `agents` table) and user session tokens (in the `user_tokens` table with a 7‑day default expiration).
- **Always verify the `Authorization` header** includes the `Bearer ` prefix before sending to `utils._extract_token`.
- **Rotate agent tokens atomically:** Update client configurations immediately after calling `services._issue_agent_token`.
- **Monitor expiration:** Query `expires_at` in `user_tokens` and ensure `utils.cleanup_expired_tokens` uses UTC ISO‑8601 timestamps.
- **Validate wallet addresses** before attempting token recovery via `utils.validate_address`.

## Frequently Asked Questions

### Why does my agent token suddenly return 401 after a restart?

The token was likely rotated or the `agents` table was updated without persisting the new credential to your client configuration. Query the `token` column directly in the database to confirm the stored value matches your request header.

### How long do user session tokens remain valid?

By default, **7 days**. This TTL is hardcoded as `timedelta(days=7)` inside `services._create_user_session` (lines 82–90). Once expired, the token is removed by `utils.cleanup_expired_tokens`, forcing re-authentication.

### Can I use the same token extraction logic for external API clients?

Yes. The `utils._extract_token` function in [`service/server/utils.py`](https://github.com/HKUDS/AI-Trader/blob/main/service/server/utils.py) handles both `Bearer <token>` and raw token formats. However, for consistency with standard OAuth2 clients, always transmit `Authorization: Bearer <token>`.

### What causes "database is locked" errors during login?

SQLite’s file-level locking conflicts with concurrent writes. Token issuance lacks the retry logic found in other service methods. Switch to PostgreSQL via the provided migration script or serialize authentication requests to avoid write contention.