infiniflow/ragflow · error · AuthException
Login failed: missing Authorization header
Error message
Login failed: missing Authorization header
What it means
Thrown by the RAGFlow admin client (admin/client/user.py:75) after a successful login POST to /admin/login or /auth/login: the JSON body reported code==0, but the HTTP response carried no 'Authorization' response header, which is where the server (via sync_construct_response(auth=user.get_id())) returns the session token. The client treats a missing token as a fatal login failure even though the login itself succeeded. It is an AuthException raised client-side, not an HTTP error status.
Source
Thrown at admin/client/user.py:75
raise AuthException(f"Register failed: {msg}")
def login_user(client: HttpClient, server_type: str, email: str, password: str) -> str:
password_enc = encrypt_password(password)
payload = {"email": email, "password": password_enc}
if server_type == "admin":
response = client.request("POST", "/admin/login", use_api_base=True, auth_kind=None, json_body=payload)
else:
response = client.request("POST", "/auth/login", use_api_base=True, auth_kind=None, json_body=payload)
try:
res = response.json()
except Exception as exc:
raise AuthException(f"Login failed: invalid JSON response ({exc})") from exc
if res.get("code") != 0:
raise AuthException(f"Login failed: {res.get('message')}")
token = response.headers.get("Authorization")
if not token:
raise AuthException("Login failed: missing Authorization header")
return token
View on GitHub (pinned to 554fb1133a)
Solutions
- Verify server_type matches the target deployment (admin server vs standard API) so login POSTs to /admin/login which is guaranteed to echo the token header.
- Curl the login endpoint directly and confirm the response includes the Authorization header; if absent, upgrade the RAGFlow server so sync_construct_response sets it.
- If a reverse proxy sits in front, ensure it forwards response headers (no proxy_hide_header/proxy_pass_header misconfig for Authorization).
- As a last resort, extract the token from the JSON body (resp access_token) instead of the header, mirroring what the server sets in user.access_token.
Example fix
# before
response = client.request("POST", "/admin/login", use_api_base=True, auth_kind=None, json_body=payload)
token = response.headers.get("Authorization")
if not token:
raise AuthException("Login failed: missing Authorization header")
# after - fall back to body token set by login_admin()
res = response.json()
token = response.headers.get("Authorization") or f"Bearer {res['data']['access_token']}"
if not token:
raise AuthException("Login failed: missing Authorization header") Defensive patterns
Strategy: try-catch
Validate before calling
import requests
r = requests.post(f"{base_url}/admin/login", json=payload)
body = r.json()
has_header = "Authorization" in r.headers
has_body_token = isinstance(body.get("data"), dict) and body["data"].get("access_token")
if body.get("code") == 0 and not (has_header or has_body_token):
raise RuntimeError("Server does not return a login token; upgrade server or fix proxy header forwarding") Type guard
def login_response_has_token(response) -> bool:
if response.headers.get("Authorization"):
return True
try:
data = response.json().get("data")
except ValueError:
return False
return isinstance(data, dict) and bool(data.get("access_token")) Try / catch
from admin.client.user import AuthException
try:
token = login(client, payload)
except AuthException as e:
if "missing Authorization header" in str(e):
# token echo broken: fix proxy/server, or read data.access_token from body
raise
raise Prevention
- Pin client and server to matching RAGFlow versions so the Authorization response-header contract holds.
- Smoke-test login once at deploy time and assert the token header is present before running batch jobs.
- Document proxy config: Authorization must be passed through in responses, not only requests.
When it happens
Trigger: Calling RagFlowAdminClient login (client.request('POST', '/admin/login'|'/auth/login', auth_kind=None, json_body=payload)) where the response body has code==0 but the Authorization response header is absent. Happens when: (1) server_type is wrong so the request hits /auth/login on a deployment that does not echo the token header; (2) a reverse proxy (nginx with 'proxy_hide_header Authorization' or underscore/header filtering) strips the response header; (3) the server build predates the header-echo behavior in sync_construct_response.
Common situations: Running an older ragflow server against a newer admin client, pointing the client at the wrong base URL or server_type ('admin' vs regular auth), or deploying behind a proxy/gateway that filters Authorization headers in both directions. Also seen when a custom WSGI middleware drops headers on login responses.
Related errors
- Email and password do not match!
- User {email} inactive
- main() must be defined or exported.
- Failed to delete memory
- Failed to update memory
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/f4019148c56193e2.
Report an issue: GitHub.