OtterMind/Chat2DB · error · RequestError
repository is not allowed
Error message
repository is not allowed
What it means
Raised at relay_server.py:219 when the parsed object's "repository" field is not exactly equal to config.repository. The relay defaults that value to 'OtterMind/Chat2DB' and reads it from the RELAY_REPOSITORY environment variable (RelayConfig.from_environment, relay_server.py:69). It is an allow-list guard: only the configured repository may post, which prevents cross-repo message injection. Returned as HTTP 403.
Source
Thrown at script/github/qq_relay/relay_server.py:220
raise RequestError(HTTPStatus.UNSUPPORTED_MEDIA_TYPE, "Content-Type must be JSON")
try:
content_length = int(self.headers.get("Content-Length", ""))
except ValueError as error:
raise RequestError(HTTPStatus.LENGTH_REQUIRED, "Content-Length is required") from error
if content_length < 1 or content_length > MAX_REQUEST_BYTES:
raise RequestError(HTTPStatus.REQUEST_ENTITY_TOO_LARGE, "request body is too large")
try:
payload = json.loads(self.rfile.read(content_length).decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as error:
raise RequestError(HTTPStatus.BAD_REQUEST, "request body is not valid JSON") from error
if not isinstance(payload, Mapping):
raise RequestError(HTTPStatus.BAD_REQUEST, "request body must be a JSON object")
return payload
def _validate_payload(self, payload: Mapping[str, Any]) -> tuple[str, str]:
config = self.relay_state.config
if payload.get("repository") != config.repository:
raise RequestError(HTTPStatus.FORBIDDEN, "repository is not allowed")
delivery_id = payload.get("delivery_id")
if not isinstance(delivery_id, str) or not DELIVERY_ID_PATTERN.fullmatch(delivery_id):
raise RequestError(HTTPStatus.BAD_REQUEST, "delivery_id is invalid")
message = payload.get("message")
if not isinstance(message, str) or not message.strip():
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"})
View on GitHub (pinned to 5ee1e990e7)
Solutions
- Set the payload "repository" to exactly match the relay's RELAY_REPOSITORY (default 'OtterMind/Chat2DB'), including case.
- Align RELAY_REPOSITORY on the relay with the repo that actually sends events.
- Strip surrounding whitespace and recheck; treat comparison as case-sensitive exact.
Example fix
# before
payload["repository"] = "ottermind/chat2db" # wrong case
# after
payload["repository"] = os.environ.get("RELAY_REPOSITORY", "OtterMind/Chat2DB") Defensive patterns
Strategy: validation
Validate before calling
expected_repo = os.environ.get('RELAY_REPOSITORY', 'OtterMind/Chat2DB')
if payload.get('repository') != expected_repo:
payload['repository'] = expected_repo # or fail fast Type guard
def repository_matches(payload: dict, expected: str) -> bool:
return isinstance(payload.get('repository'), str) and payload['repository'] == expected Try / catch
resp = requests.post(url, json=payload)
if resp.status_code == 403: # repository not allowed
# reconcile payload['repository'] with the relay's RELAY_REPOSITORY Prevention
- Source 'repository' from the same config/env the relay uses.
- Treat the comparison as case-sensitive exact match with no surrounding whitespace.
When it happens
Trigger: POST body whose "repository" is a different owner/name, is missing, or differs in casing/whitespace from RELAY_REPOSITORY (e.g. 'ottermind/chat2db' vs 'OtterMind/Chat2DB').
Common situations: Webhook/action hardcodes a repo string that drifted from the relay's RELAY_REPOSITORY; repo was renamed or forked; relay deployed for repo A while sender emits repo B.
Related errors
- message is too long
- not found
- request body must be a JSON object
- delivery_id is invalid
- message must be non-empty text
AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14).
Data as JSON: /api/errors/ea83f482b56f0f8b.
Report an issue: GitHub.