fix(auth): show subscription setup errors

Preserve subscription-auth failures instead of rewriting them as missing API keys, and point Claude/Codex subscription users at the correct login and provider commands.

Fixes #254
This commit is contained in:
tjb-tech
2026-05-24 09:01:35 +00:00
parent c281d34c4a
commit fea0b75bc4
2 changed files with 51 additions and 7 deletions
+35 -7
View File
@@ -198,13 +198,8 @@ def _resolve_api_client_from_settings(settings) -> SupportsStreamingMessages:
def _safe_resolve_auth():
try:
return settings.resolve_auth()
except (ValueError, Exception):
print(
"Error: No API key configured.\n"
" Run `oh auth login` to set up authentication, or set the\n"
" ANTHROPIC_API_KEY (or OPENAI_API_KEY) environment variable.",
file=sys.stderr,
)
except Exception as exc:
_print_auth_resolution_error(settings, exc)
raise SystemExit(1)
if settings.api_format == "copilot":
@@ -243,6 +238,39 @@ def _resolve_api_client_from_settings(settings) -> SupportsStreamingMessages:
)
def _print_auth_resolution_error(settings, exc: Exception) -> None:
"""Render auth failures without collapsing subscription errors into API-key advice."""
try:
profile_name, profile = settings.resolve_profile()
auth_source = (getattr(profile, "auth_source", "") or "").strip()
except Exception:
profile_name = ""
auth_source = ""
message = str(exc).strip() or exc.__class__.__name__
if auth_source in {"claude_subscription", "codex_subscription"}:
login_command = "claude-login" if auth_source == "claude_subscription" else "codex-login"
provider_name = profile_name or (
"claude-subscription" if auth_source == "claude_subscription" else "codex"
)
print(
f"Error: {message}\n"
f" This profile uses subscription auth, not an API key.\n"
f" Run `oh auth {login_command}` to bind the local CLI session, then\n"
f" run `oh provider use {provider_name}` to activate it.",
file=sys.stderr,
)
return
print(
"Error: No API key configured.\n"
f" {message}\n"
" Run `oh auth login` to set up authentication, or set the\n"
" ANTHROPIC_API_KEY (or OPENAI_API_KEY) environment variable.",
file=sys.stderr,
)
async def build_runtime(
*,
prompt: str | None = None,
+16
View File
@@ -31,3 +31,19 @@ async def test_build_runtime_exits_cleanly_for_openai_format(monkeypatch):
with pytest.raises(SystemExit, match="1"):
await build_runtime(active_profile="openai-compatible", api_format="openai")
@pytest.mark.asyncio
async def test_build_runtime_reports_subscription_auth_setup(monkeypatch, tmp_path, capsys):
"""Subscription profiles should not be reported as missing API keys."""
monkeypatch.setenv("OPENHARNESS_CONFIG_DIR", str(tmp_path / "config"))
monkeypatch.delenv("ANTHROPIC_AUTH_TOKEN", raising=False)
with pytest.raises(SystemExit, match="1"):
await build_runtime(active_profile="claude-subscription")
captured = capsys.readouterr()
assert "subscription auth, not an API key" in captured.err
assert "oh auth claude-login" in captured.err
assert "oh provider use claude-subscription" in captured.err
assert "No API key configured" not in captured.err