{"record":{"id":"3c119f54e4ab00e8","repo":"OtterMind/Chat2DB","slug":"message-is-too-long","errorCode":null,"errorMessage":"message is too long","messagePattern":"message is too long","errorType":"http","errorClass":"RequestError","httpStatus":400,"severity":"error","filePath":"script/github/qq_relay/relay_server.py","lineNumber":228,"sourceCode":"            payload = json.loads(self.rfile.read(content_length).decode(\"utf-8\"))\n        except (UnicodeDecodeError, json.JSONDecodeError) as error:\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"request body is not valid JSON\") from error\n        if not isinstance(payload, Mapping):\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"request body must be a JSON object\")\n        return payload\n\n    def _validate_payload(self, payload: Mapping[str, Any]) -> tuple[str, str]:\n        config = self.relay_state.config\n        if payload.get(\"repository\") != config.repository:\n            raise RequestError(HTTPStatus.FORBIDDEN, \"repository is not allowed\")\n        delivery_id = payload.get(\"delivery_id\")\n        if not isinstance(delivery_id, str) or not DELIVERY_ID_PATTERN.fullmatch(delivery_id):\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"delivery_id is invalid\")\n        message = payload.get(\"message\")\n        if not isinstance(message, str) or not message.strip():\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"message must be non-empty text\")\n        if len(message) > config.max_message_length:\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"message is too long\")\n        if CONTROL_CHARACTER_PATTERN.search(message):\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"message contains control characters\")\n        return delivery_id, message\n\n    def do_GET(self) -> None:  # noqa: N802\n        if self.path == \"/healthz\":\n            self._send_json(HTTPStatus.OK, {\"ok\": True})\n            return\n        self._send_json(HTTPStatus.NOT_FOUND, {\"error\": \"not found\"})\n\n    def do_POST(self) -> None:  # noqa: N802\n        delivery_id = \"\"\n        reserved = False\n        try:\n            if self.path != \"/v1/qq/github\":\n                raise RequestError(HTTPStatus.NOT_FOUND, \"not found\")\n            self._authorize()\n            delivery_id, message = self._validate_payload(self._read_payload())","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/script/github/qq_relay/relay_server.py#L210-L246","documentation":"Raised at relay_server.py:227 when len(message) > config.max_message_length. The cap defaults to 900 (RelayConfig.max_message_length) and is read from the RELAY_MAX_MESSAGE_LENGTH environment variable. It protects the QQ/NapCat group message size limit. len() counts Unicode code points, not bytes. Returned as HTTP 400.","triggerScenarios":"A single delivery whose message text is longer than the configured cap, e.g. a full commit diff, log dump, or concatenated PR body forwarded as one message.","commonSituations":"Default 900 is too small for verbose events and RELAY_MAX_MESSAGE_LENGTH was not raised; sender forwards raw artifact text without truncation; emoji/CJK counts as one code point each so byte-based truncation underestimates.","solutions":["Truncate the message to <= max_message_length code points on the sender before posting.","Raise RELAY_MAX_MESSAGE_LENGTH on the relay to stay within QQ's real limit.","Split very long content into multiple deliveries with distinct delivery_ids."],"exampleFix":"# before\npayload[\"message\"] = long_text\n# after\npayload[\"message\"] = long_text[:900]  # or read RELAY_MAX_MESSAGE_LENGTH from relay","handlingStrategy":"validation","validationCode":"max_len = int(os.environ.get('RELAY_MAX_MESSAGE_LENGTH', '900'))\npayload['message'] = payload['message'][:max_len]","typeGuard":"def message_within_limit(payload: dict, limit: int) -> bool:\n    msg = payload.get('message')\n    return isinstance(msg, str) and len(msg) <= limit","tryCatchPattern":"resp = requests.post(url, json=payload)\nif resp.status_code == 400 and resp.json().get('error') == 'message is too long':\n    payload['message'] = payload['message'][:900]\n    resp = requests.post(url, json=payload)","preventionTips":["Truncate by code points (len on str), not bytes, to match the relay.","Keep sender's limit in sync with the relay's RELAY_MAX_MESSAGE_LENGTH."],"tags":["http","validation","configuration","qq-relay"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}