diff --git a/changes/10732.fix.md b/changes/10732.fix.md new file mode 100644 index 00000000000..cb27913b649 --- /dev/null +++ b/changes/10732.fix.md @@ -0,0 +1 @@ +Fix AttributeError in v2 login CLI when server returns a string in the data field (BA-5559) diff --git a/src/ai/backend/client/cli/v2/login_cmd.py b/src/ai/backend/client/cli/v2/login_cmd.py index 0a241863710..4e12ff574d0 100644 --- a/src/ai/backend/client/cli/v2/login_cmd.py +++ b/src/ai/backend/client/cli/v2/login_cmd.py @@ -48,15 +48,30 @@ async def _run() -> None: async with client.session.post(login_url, json=payload) as resp: data = await resp.json() + def _get_details(resp_data: dict[str, Any]) -> str: + raw = resp_data.get("data") + if isinstance(raw, dict): + details = raw.get("details") + if details: + return str(details) + elif raw: + return str(raw) + # Handle RFC 7807 problem responses (e.g., {"type": ..., "title": ...}) + if "title" in resp_data: + return str(resp_data["title"]) + return "Unknown error" + if not data.get("authenticated"): - if data.get("data", {}).get("details") == "OTP not provided": + details = _get_details(data) + + if details == "OTP not provided": otp = input("One-time Password: ") payload["otp"] = otp.strip() async with client.session.post(login_url, json=payload) as resp: data = await resp.json() if not data.get("authenticated"): - details = data.get("data", {}).get("details", "Unknown error") + details = _get_details(data) click.echo(click.style(f"Login failed: {details}", fg="red")) raise SystemExit(1)