OtterMind/Chat2DB · critical · RuntimeError

QQ_GROUP_ID must be a numeric QQ group number

Error message

QQ_GROUP_ID must be a numeric QQ group number

What it means

Raised by RelayConfig.from_environment (relay_server.py:64) as a RuntimeError when QQ_GROUP_ID is not composed entirely of digits (str.isdigit() is false). The group id is parsed with int() and sent to OneBot, so a non-numeric value is fatal at startup.

Source

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

    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
        self.window_seconds = window_seconds
        self._timestamps: deque[float] = deque()
        self._lock = threading.Lock()

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Set QQ_GROUP_ID to the numeric QQ group number (digits only), e.g. 123456789.
  2. Remove any quotes, spaces, or '+' prefix from the value.
  3. Confirm it is a group id, not a personal QQ account.

Example fix

# before: QQ_GROUP_ID="+123456789"
# after:  QQ_GROUP_ID=123456789
Defensive patterns

Strategy: validation

Validate before calling

import os
group_id = os.environ.get("QQ_GROUP_ID", "")
if not group_id.isdigit():
    raise SystemExit("QQ_GROUP_ID must be digits only, e.g. 123456789")

Type guard

def is_numeric_group_id(value: str) -> bool:
    return value.isdigit()

Prevention

When it happens

Trigger: Starting the relay with QQ_GROUP_ID unset (empty string fails isdigit), containing spaces, a leading '+', or non-digit characters. Checked at startup.

Common situations: QQ_GROUP_ID copied with surrounding whitespace or quotes; the group number pasted with a prefix; the env var unset; using a QQ number (user) instead of a group number.

Related errors


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