Repository navigation
Conversation
With no usable key for the active model's provider, the runner printed 'OPENAI_KEY not set' and carried on, so every tool call failed against the placeholder a full run later (The-PR-Agent#4195, The-PR-Agent#4324) - the case doctor from The-PR-Agent#4321 cannot reach, since those are unattended Action runs. Gate the runner with the same resolution doctor uses (shared missing_provider_key_reason), after the env-to-settings pass so a provided key counts: on failure print one line naming the missing variable, mark the action failed, and return before the event payload is even read. Keyless providers defer to the run as before, and the troubleshooting entry now describes the early stop.
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
⚙️ Agent run details
|
PR Code Suggestions ✨Explore these optional code suggestions:
⚙️ Agent run details
|
A repo-level .pr_agent.toml can select a different provider with its own key, so judging the active provider before those settings load could stop a correctly configured deployment (/improve finding on The-PR-Agent#4325). Move the gate to right after apply_repo_settings - still ahead of every tool - and assert the order in the gate tests.
CodeQL's taint analysis cannot prove the reason string never carries key material; route it through redact_credentials like the doctor output path, which the same query accepts.
ef1d44c to
b1b12f3
Compare
CodeQL's clear-text-logging query cannot vouch for any string derived near key handling, even through redact_credentials (opaque to it), so the print kept failing the check. Raise SystemExit with the reason instead: the job's error line carries it, no logging sink is involved, and the exit stays non-zero.
…data Two layers, so the message cannot be a leak even in principle: the gate now renders only env var names from the constant PROVIDER_API_KEY_ENV_VARS table plus static text (the provider from the model id is used as a lookup key and never rendered), and it is raised as SystemExit - the CodeQL clear-text-logging query sinks logging calls, print and sys.stdout/stderr.write, none of which a raise reaches. missing_provider_key_reason becomes missing_provider_key_env_vars, returning (provider, env var names); doctor keeps its full detail line.
|
The CodeQL round: the query taints anything derived near key handling, and treats redact_credentials as opaque, so printing the reason kept failing regardless of its actual content. The gate now renders only env var names from the constant provider table (the provider itself is a lookup key, never rendered) and raises SystemExit instead of printing — the query's sinks are logging, print and stdout/stderr writes, none of which a raise reaches. Verified against the CleartextLogging sink definitions in the CodeQL source. |
IsmaelMartinez
left a comment
There was a problem hiding this comment.
Thanks @Yoel-qin for moving the gate after repo settings, which the bot caught first, and for keeping the message to env-var names only. One ask before merge: the gate stops setups that run fine today. Probing missing_provider_key_env_vars() with no key, an OPENAI.API_BASE gateway, Bedrock on the AWS credential chain and Ollama with its api_base all return missing, because the table lists an optional key for each. Suggestion inline, verified against the PR's tests.
The keyless branch in _check_provider_key intended ambient auth (Azure AD, Bedrock IAM) and custom endpoints to be judged by the run itself, but the shared gate function stopped on them because only the env-table key was consulted. Honor AZURE_AD.CLIENT_ID, bedrock providers, and a provider-scoped api_base in missing_provider_key_env_vars(), with one test per case.
IsmaelMartinez
left a comment
There was a problem hiding this comment.
Thanks for applying this and adding a test per case. My earlier suggestion missed two Azure paths the handler supports: companion credentials (AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET) and AZURE_AD_TOKEN, so those runs still stop. Deferring azure like bedrock covers both (suggestion below); could you add a test for each?
| ok, _detail = _check_provider_key(LiteLLMAIHandler(), provider) | ||
| # Ambient auth (Azure AD, the AWS credential chain) or a custom endpoint needs no static key: the run decides. | ||
| settings = get_settings() | ||
| ambient = settings.get("AZURE_AD.CLIENT_ID", None) or provider.startswith("bedrock") |
There was a problem hiding this comment.
| ambient = settings.get("AZURE_AD.CLIENT_ID", None) or provider.startswith("bedrock") | |
| ambient = settings.get("AZURE_AD.CLIENT_ID", None) or provider.startswith(("bedrock", "azure")) |
There was a problem hiding this comment.
Applied — azure defers on its own like bedrock now, with a test for azure with no Azure AD settings next to the others. AZURE_AD.CLIENT_ID still covers the configured-client case for any provider.
An azure deployment can authenticate through ambient credentials (managed identity, DefaultAzureCredential) without a static key or Azure AD settings, so it defers to the run like bedrock does.
IsmaelMartinez
left a comment
There was a problem hiding this comment.
Thanks @Yoel-qin for deferring azure and adding its test; the ambient-auth cases all pass now. One ask: on issue_comment and workflow_run the gate runs before the repo's .pr_agent.toml is applied, so a repo that picks its provider there gets stopped against the default model on every /review comment. Main dispatches the same comment. Details inline.
| # .pr_agent.toml can select a different provider) but before any tool runs: carrying | ||
| # on with the placeholder only fails every call a full run later (#4195, #4324). | ||
| # Keyless providers defer to the run, as in `pr-agent doctor`. | ||
| missing_key = missing_provider_key_env_vars() |
There was a problem hiding this comment.
On comment events pr_url at line 247 is empty, so repo settings are not applied when this gate runs. With anthropic/... in .pr_agent.toml and only ANTHROPIC_API_KEY set, a /review comment stops here. Line 247 is outside the diff, so here it is as code:
pull_request = event_payload.get("pull_request") or event_payload.get("issue", {}).get("pull_request") or {}
pr_url = pull_request.get("html_url")workflow_run applies its settings at line 484, after this gate, so it needs the same treatment. Could you add an issue_comment case to test_repository_settings_are_applied_before_key_gate?
There was a problem hiding this comment.
Good catch, thanks. I moved the URL lookup into a _settings_pr_url helper that handles all three payload shapes, so comment and workflow_run events get their repo settings applied before the gate too. The duplicate apply_repo_settings in the workflow_run branch is gone. Ordering test now has issue_comment and workflow_run cases.
…event shape Comment events carry the PR under issue.pull_request, so the top-level lookup found no url and the gate judged the default provider before the repo-level settings (applied later, inside handle_request) selected the real one — a /review comment on an anthropic-configured repo with only ANTHROPIC_API_KEY was stopped. workflow_run applied its settings after the gate for the same wrong judgment. Resolve the event's PR in any shape (top-level pull_request, a comment's issue.pull_request, a pull-request-originated workflow_run's pull_requests[0]) in one helper used before the gate, and drop the now duplicate apply in the workflow_run branch. Order tests cover issue_comment and workflow_run next to the PR case.
IsmaelMartinez
left a comment
There was a problem hiding this comment.
Thanks @Yoel-qin, the _settings_pr_url helper covers comment and workflow_run events nicely. One ask I should have raised last round: events with no PR behind them (a comment on a plain issue, a workflow_run started by a push) still reach the gate with the default model, so a repo that sets its provider in .pr_agent.toml gets a red run where main stays green. Gating only when a PR URL resolved fixes both; suggestions inline.
| try: | ||
| get_logger().info("Applying repo settings") | ||
| pr_url = event_payload.get("pull_request", {}).get("html_url") | ||
| pr_url = _settings_pr_url(event_payload, GITHUB_EVENT_NAME) |
There was a problem hiding this comment.
Resolve the url outside the try so the gate below can use it:
| try: | |
| get_logger().info("Applying repo settings") | |
| pr_url = event_payload.get("pull_request", {}).get("html_url") | |
| pr_url = _settings_pr_url(event_payload, GITHUB_EVENT_NAME) | |
| pr_url = _settings_pr_url(event_payload, GITHUB_EVENT_NAME) | |
| try: | |
| get_logger().info("Applying repo settings") |
| # .pr_agent.toml can select a different provider) but before any tool runs: carrying | ||
| # on with the placeholder only fails every call a full run later (#4195, #4324). | ||
| # Keyless providers defer to the run, as in `pr-agent doctor`. | ||
| missing_key = missing_provider_key_env_vars() |
There was a problem hiding this comment.
No PR, no repo settings: let the run decide, as main does.
| missing_key = missing_provider_key_env_vars() | |
| missing_key = missing_provider_key_env_vars() if pr_url else None |
There was a problem hiding this comment.
Done — no PR means the gate stays out of the way, like on main. Added a test for both shapes: a plain issue comment and a push-origin workflow_run.
Resolve the settings PR url before the try block so the gate can see it, and only gate when one was found: a plain issue comment or a push-origin workflow_run has no repo settings to judge from, and main lets those runs decide on their own. Tests cover both no-PR shapes.
Problem
With no usable key for the active model's provider, the Action runner printed
OPENAI_KEY not setand carried on, so every tool call failed against the placeholder withIncorrect API key provided: dummy_key— one full run later (#4195: 20 of 81 sampled owners).doctor(#4321) preflights this by hand, but unattended Action runs got no gate — the follow-up agreed in #4321 (#4324).Solution
doctorexposes its key resolution as a sharedmissing_provider_key_env_vars(): for the active provider ofconfig.model, it returns the missing env-variable names (a placeholder key counts as missing), orNonewhen the run can proceed — a usable key, ambient auth (Azure AD, the AWS credential chain), or a provider-scopedapi_base, which only the run itself can judge.pull_request, a comment'sissue.pull_request, or a workflow_run'spull_requests[0]— applies repo settings from it, then gates on the active provider's key before any tool runs: on failure it raisesSystemExitnaming the env variables to set; the reason becomes the job's error line and nothing key-derived flows through a print or log sink (CodeQL clean).OPENAI_KEY not setprint-and-continue branch is gone — the gate subsumes it, and non-OpenAI setups no longer see the misleading OpenAI-specific warning.dummy_keynow describes the early stop instead of the old symptom.Testing
Nonewith a key, namesOPENAI_API_KEYwithout one, and defers to the run for Azure ambient auth (with and without Azure AD settings), Bedrock, and a customapi_base— one test each.Fixes #4324