Skip to content

Commit fea0b75

Browse files
committed
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
1 parent c281d34 commit fea0b75

2 files changed

Lines changed: 51 additions & 7 deletions

File tree

src/openharness/ui/runtime.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,8 @@ def _resolve_api_client_from_settings(settings) -> SupportsStreamingMessages:
198198
def _safe_resolve_auth():
199199
try:
200200
return settings.resolve_auth()
201-
except (ValueError, Exception):
202-
print(
203-
"Error: No API key configured.\n"
204-
" Run `oh auth login` to set up authentication, or set the\n"
205-
" ANTHROPIC_API_KEY (or OPENAI_API_KEY) environment variable.",
206-
file=sys.stderr,
207-
)
201+
except Exception as exc:
202+
_print_auth_resolution_error(settings, exc)
208203
raise SystemExit(1)
209204

210205
if settings.api_format == "copilot":
@@ -243,6 +238,39 @@ def _safe_resolve_auth():
243238
)
244239

245240

241+
def _print_auth_resolution_error(settings, exc: Exception) -> None:
242+
"""Render auth failures without collapsing subscription errors into API-key advice."""
243+
try:
244+
profile_name, profile = settings.resolve_profile()
245+
auth_source = (getattr(profile, "auth_source", "") or "").strip()
246+
except Exception:
247+
profile_name = ""
248+
auth_source = ""
249+
250+
message = str(exc).strip() or exc.__class__.__name__
251+
if auth_source in {"claude_subscription", "codex_subscription"}:
252+
login_command = "claude-login" if auth_source == "claude_subscription" else "codex-login"
253+
provider_name = profile_name or (
254+
"claude-subscription" if auth_source == "claude_subscription" else "codex"
255+
)
256+
print(
257+
f"Error: {message}\n"
258+
f" This profile uses subscription auth, not an API key.\n"
259+
f" Run `oh auth {login_command}` to bind the local CLI session, then\n"
260+
f" run `oh provider use {provider_name}` to activate it.",
261+
file=sys.stderr,
262+
)
263+
return
264+
265+
print(
266+
"Error: No API key configured.\n"
267+
f" {message}\n"
268+
" Run `oh auth login` to set up authentication, or set the\n"
269+
" ANTHROPIC_API_KEY (or OPENAI_API_KEY) environment variable.",
270+
file=sys.stderr,
271+
)
272+
273+
246274
async def build_runtime(
247275
*,
248276
prompt: str | None = None,

tests/test_ui/test_runtime_api_key.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,19 @@ def fake_resolve_auth(self):
3131

3232
with pytest.raises(SystemExit, match="1"):
3333
await build_runtime(active_profile="openai-compatible", api_format="openai")
34+
35+
36+
@pytest.mark.asyncio
37+
async def test_build_runtime_reports_subscription_auth_setup(monkeypatch, tmp_path, capsys):
38+
"""Subscription profiles should not be reported as missing API keys."""
39+
monkeypatch.setenv("OPENHARNESS_CONFIG_DIR", str(tmp_path / "config"))
40+
monkeypatch.delenv("ANTHROPIC_AUTH_TOKEN", raising=False)
41+
42+
with pytest.raises(SystemExit, match="1"):
43+
await build_runtime(active_profile="claude-subscription")
44+
45+
captured = capsys.readouterr()
46+
assert "subscription auth, not an API key" in captured.err
47+
assert "oh auth claude-login" in captured.err
48+
assert "oh provider use claude-subscription" in captured.err
49+
assert "No API key configured" not in captured.err

0 commit comments

Comments
 (0)