OtterMind/Chat2DB · critical · RuntimeError

RELAY_TOKEN must contain at least 32 characters

Error message

RELAY_TOKEN must contain at least 32 characters

What it means

Raised by RelayConfig.from_environment (relay_server.py:60) as a RuntimeError when RELAY_TOKEN has fewer than 32 characters. The relay authenticates inbound requests by exact Bearer-token comparison, so a short token is treated as misconfiguration and the server refuses to start.

Source

Thrown at script/github/qq_relay/relay_server.py:60

@dataclass(frozen=True)
class RelayConfig:
    relay_token: str
    onebot_token: str
    onebot_url: str
    repository: str
    group_id: int
    max_message_length: int = 900
    rate_limit: int = 30
    rate_window_seconds: int = 60

    @classmethod
    def from_environment(cls) -> "RelayConfig":
        relay_token = os.environ.get("RELAY_TOKEN", "")
        onebot_token = os.environ.get("ONEBOT_TOKEN", "")
        group_id = os.environ.get("QQ_GROUP_ID", "")
        if len(relay_token) < 32:
            raise RuntimeError("RELAY_TOKEN must contain at least 32 characters")
        if len(onebot_token) < 32:
            raise RuntimeError("ONEBOT_TOKEN must contain at least 32 characters")
        if not group_id.isdigit():
            raise RuntimeError("QQ_GROUP_ID must be a numeric QQ group number")
        return cls(
            relay_token=relay_token,
            onebot_token=onebot_token,
            onebot_url=os.environ.get("ONEBOT_URL", "http://napcat:3000"),
            repository=os.environ.get("RELAY_REPOSITORY", "OtterMind/Chat2DB"),
            group_id=int(group_id),
            max_message_length=int(os.environ.get("RELAY_MAX_MESSAGE_LENGTH", "900")),
            rate_limit=int(os.environ.get("RELAY_RATE_LIMIT", "30")),
        )


class RateLimiter:
    def __init__(self, limit: int, window_seconds: int):
        self.limit = limit

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Generate a token of at least 32 characters, e.g. python -c "import secrets;print(secrets.token_urlsafe(48))".
  2. Set RELAY_TOKEN in the relay's environment (Compose env, systemd, or container env).
  3. Use the same value for QQ_RELAY_TOKEN in the GitHub Actions notifier.

Example fix

RELAY_TOKEN=$(python -c 'import secrets;print(secrets.token_urlsafe(48))')
Defensive patterns

Strategy: validation

Validate before calling

import os, secrets
token = os.environ.get("RELAY_TOKEN", "")
if len(token) < 32:
    token = secrets.token_urlsafe(48)
    print(f"RELAY_TOKEN too short; generated a new one (set it permanently): {token}")

Type guard

def is_strong_token(value: str) -> bool:
    return len(value) >= 32

Prevention

When it happens

Trigger: Starting the relay server with RELAY_TOKEN unset, empty, or shorter than 32 chars. from_environment is called once at startup in main(), so this is fatal before the server binds.

Common situations: RELAY_TOKEN not set in the container/host environment; a short placeholder value used during setup; the secret truncated when copied; deploying without the env var mounted.

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/143f63b8f2e743d9. Report an issue: GitHub.