HKUDS/Vibe-Trading · critical · HTTPException
Invalid or missing API key
Error message
Invalid or missing API key
What it means
Raised by the local shutdown control-plane endpoint when an API_AUTH_KEY is configured but the request did not present a matching credential in the Authorization header (query-string keys are explicitly rejected here via allow_query=False). The comparison uses hmac.compare_digest, so both a missing token and any incorrect token produce the same 401. It is a deliberate guard so that a destructive shutdown action cannot be triggered anonymously even on a key-protected deployment.
Source
Thrown at agent/src/api/security.py:445
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Cross-site request denied")
origin = request.headers.get("origin")
if origin and not (_is_loopback_origin(origin) or _origin_matches_request_host(origin, request)):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Cross-site request denied")
def _require_shutdown_authorization(
*,
request: Request,
cred: Optional[HTTPAuthorizationCredentials],
) -> None:
"""Authorize the local shutdown control-plane action."""
_reject_cross_site_browser_request(request)
api_key = _configured_api_key()
if api_key:
token = _auth_credential_from_header_or_query(cred, None, allow_query=False)
if not token or not hmac.compare_digest(token, api_key):
raise HTTPException(status_code=401, detail="Invalid or missing API key")
return
if not _is_local_client(request):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="API_AUTH_KEY is required for non-local API access",
)
#: Subject recorded when the caller proved possession of the shared API key.
#: It is a role, not a person: every holder of that one secret authenticates
#: identically, so this string must never be presented as an identity.
SHARED_KEY_SUBJECT = "shared-key-holder"
#: Subject recorded when no key is configured and a loopback client was trusted.
LOOPBACK_SUBJECT = "loopback-operator"
def _validate_api_auth(View on GitHub (pinned to 80ffdda44c)
Solutions
- Send the exact configured API_AUTH_KEY in the Authorization header (e.g. Authorization: Bearer <key>) — query-string auth is not accepted here
- Verify the value being sent matches the server's API_AUTH_KEY byte-for-byte (print repr() of both; check for trailing newlines/whitespace from .env files)
- If the key lives in .env or a secret manager, confirm the running process actually loaded it (print/inspect env of the server process)
- Rotate the key on both sides if it was recently changed, and confirm the client is not caching an old value
Example fix
# before
import requests
requests.post("http://localhost:8000/shutdown", params={"api_key": KEY}) # 401: query auth not allowed
# after
import requests
requests.post("http://localhost:8000/shutdown", headers={"Authorization": f"Bearer {KEY}"}) Defensive patterns
Strategy: validation
Validate before calling
import os, hmac
KEY = os.environ.get("API_AUTH_KEY", "")
assert KEY, "API_AUTH_KEY not set on client side"
headers = {"Authorization": f"Bearer {KEY}"}
# sanity: length matches what was provisioned
assert len(KEY) > 0 and KEY == KEY.strip(), "key has stray whitespace" Try / catch
try:
resp = requests.post(f"{BASE}/shutdown", headers=headers)
resp.raise_for_status()
except requests.HTTPError as e:
if e.response.status_code == 401:
raise RuntimeError("Shutdown auth failed: check API_AUTH_KEY on both sides (header-only)") from e
raise Prevention
- Centralize key handling in one client helper that always uses the Authorization header
- Strip/validate keys at config load time to catch whitespace mismatches early
- Never pass keys in URLs; they leak into logs and are rejected on sensitive routes
When it happens
Trigger: Calling POST on the local shutdown route (shutdown_local_api -> _require_shutdown_authorization) while API_AUTH_KEY is set, with either no Authorization header, a malformed header, or a key that does not byte-for-byte match the configured API_AUTH_KEY. Passing the key as ?api_key=... also triggers it because query auth is disabled for this route.
Common situations: Env var API_AUTH_KEY has trailing whitespace or quotes in one environment but not the other; the client sends a stale key after the server key was rotated; a script tries to pass the key in the URL (works on other routes but not this one); the key was set in the server's shell but not in the service's systemd/docker environment.
Related errors
- API_AUTH_KEY is required for non-local API access
- Settings access requires API_AUTH_KEY or a local loopback cl
- Settings writes require API_AUTH_KEY or a local loopback cli
- This file type is not allowed for upload.
- Feishu / Lark registration does not support client_secret au
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/8d4322dc3a9d4118.
Report an issue: GitHub.