OtterMind/Chat2DB · warning · RequestError

not found

Error message

not found

What it means

Raised at relay_server.py:244 (POST) and relay_server.py:237 (GET) for any path that is not an allowed route. The relay serves exactly two: POST /v1/qq/github and GET /healthz. Anything else returns HTTP 404 with {"error": "not found"}.

Source

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

            raise RequestError(HTTPStatus.BAD_REQUEST, "message must be non-empty text")
        if len(message) > config.max_message_length:
            raise RequestError(HTTPStatus.BAD_REQUEST, "message is too long")
        if CONTROL_CHARACTER_PATTERN.search(message):
            raise RequestError(HTTPStatus.BAD_REQUEST, "message contains control characters")
        return delivery_id, message

    def do_GET(self) -> None:  # noqa: N802
        if self.path == "/healthz":
            self._send_json(HTTPStatus.OK, {"ok": True})
            return
        self._send_json(HTTPStatus.NOT_FOUND, {"error": "not found"})

    def do_POST(self) -> None:  # noqa: N802
        delivery_id = ""
        reserved = False
        try:
            if self.path != "/v1/qq/github":
                raise RequestError(HTTPStatus.NOT_FOUND, "not found")
            self._authorize()
            delivery_id, message = self._validate_payload(self._read_payload())
            existing = self.relay_state.deliveries.reserve(delivery_id)
            if existing is not None:
                if existing == "pending":
                    raise RequestError(
                        HTTPStatus.SERVICE_UNAVAILABLE, "delivery is still in progress"
                    )
                self._send_json(
                    HTTPStatus.OK,
                    {
                        "ok": True,
                        "duplicate": True,
                        "message_id": existing,
                    },
                )
                return
            reserved = True

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. POST to exactly /v1/qq/github and GET health at /healthz.
  2. Build the URL from a base with no trailing slash plus the literal path.
  3. Confirm no proxy rewrites the path or strips/keeps a trailing slash.

Example fix

# before
requests.post(f"{base}/v1/qq/github/", json=payload)
# after
requests.post(f"{base.rstrip("/")}/v1/qq/github", json=payload)
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urljoin
endpoint = urljoin(base.rstrip('/') + '/', 'v1/qq/github')
# endpoint is now '<base>/v1/qq/github' with no trailing slash

Type guard

def is_relay_post_path(url: str) -> bool:
    return url.rstrip('/').endswith('/v1/qq/github')

Try / catch

resp = requests.post(url, json=payload)
if resp.status_code == 404:
    url = url.rstrip('/') + '/v1/qq/github'
    resp = requests.post(url, json=payload)

Prevention

When it happens

Trigger: POST to a typo'd or wrong path (/v1/github, /v1/qq, /qq/github), with a trailing slash (/v1/qq/github/), a doubled slash, or a wrong version (/v2/...); GET to /.

Common situations: Base URL misconfigured in the action/workflow; trailing slash added by urljoin or a proxy; endpoint renamed across a release; hitting the relay root to probe it.

Related errors


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