OtterMind/Chat2DB · error · ConfigurationError

Prepared QQ notification message is invalid

Error message

Prepared QQ notification message is invalid

What it means

Raised by _read_prepared_message (notify_qq.py:261) when the envelope's "message" is not a non-empty string of at most 900 characters. The relay and OneBot enforce a 900-char ceiling, so an envelope violating it is rejected before sending.

Source

Thrown at script/github/notify_qq.py:261


def _read_prepared_message(path: Path, repository: str) -> tuple[str, str]:
    if not path.is_file() or path.stat().st_size > 4096:
        raise ConfigurationError("Prepared QQ notification is missing or too large")
    try:
        envelope = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, UnicodeError, json.JSONDecodeError) as error:
        raise ConfigurationError("Prepared QQ notification is not valid JSON") from error
    if not isinstance(envelope, Mapping) or envelope.get("version") != 1:
        raise ConfigurationError("Prepared QQ notification has an invalid format")
    if envelope.get("repository") != repository:
        raise ConfigurationError("Prepared QQ notification repository does not match")
    event_name = str(envelope.get("event_name") or "")
    if event_name not in COLLECTED_EVENT_NAMES:
        raise ConfigurationError("Prepared QQ notification event is not allowed")
    message = envelope.get("message")
    if not isinstance(message, str) or not message or len(message) > 900:
        raise ConfigurationError("Prepared QQ notification message is invalid")
    if re.search(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]", message):
        raise ConfigurationError("Prepared QQ notification contains control characters")
    if re.search(r"(?i)\[CQ:", message):
        raise ConfigurationError("Prepared QQ notification contains a OneBot CQ code")
    return event_name, message


def _event_detail(event_name: str, action: str, payload: Mapping[str, Any]) -> str:
    if action in {"labeled", "unlabeled"}:
        label = payload.get("label") or {}
        return f"标签:{_clean_text(label.get('name'), 80)}"
    if action in {"assigned", "unassigned"}:
        return f"处理人:{_login(payload.get('assignee'))}"
    if action in {"milestoned", "demilestoned"}:
        milestone = payload.get("milestone") or {}
        return f"里程碑:{_clean_text(milestone.get('title'), 100)}"
    if action in {"review_requested", "review_request_removed"}:
        reviewer = _login(payload.get("requested_reviewer"))

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Regenerate the envelope with the current collect step so message is built and bounded by _join_message_lines (max 900).
  2. If editing by hand, set message to a non-empty string no longer than 900 characters.
  3. Discard the artifact and re-run the collect job.
Defensive patterns

Strategy: type-guard

Validate before calling

message = envelope.get("message")
if not isinstance(message, str) or not message or len(message) > 900:
    raise SystemExit("message must be a non-empty string <= 900 chars")

Type guard

def is_valid_message(envelope: object) -> bool:
    msg = envelope.get("message") if isinstance(envelope, Mapping) else None
    return isinstance(msg, str) and 0 < len(msg) <= 900

Prevention

When it happens

Trigger: The "message" field is null, a number, an empty string, a JSON array/object, or a string longer than 900 characters. Reaches here only via the QQ_PREPARED_MESSAGE_PATH read path.

Common situations: Hand-editing the JSON and omitting or corrupting message; truncating the envelope manually; an envelope from a script version that allowed a different length cap.

Related errors


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