Skip to content

Commit afcb4f1

Browse files
fix(extraction): surface live vertex-ai profile on gcloud-adc create failure (#792)
OpenShell's --from-gcloud-adc emits an identical generic "no credentials resolved" error whether the ADC content is bad or the gateway's served google-vertex-ai profile doesn't allow empty-credential bootstrap - the two cases are indistinguishable from CLI stderr alone. Every ADC-content failure mode (missing file, missing client_secret, revoked refresh_token) has been reproduced locally and each produces a distinct, more specific error, none matching what stage actually shows, and a live GetProviderProfile call captured via the new sticky-runtime probe returned a clean 200 in under 1ms right before the same generic error - ruling out an RPC failure. Dump the gateway's actual served profile via `provider profile export` inline in the error on failure so the next occurrence captured by the sticky-runtime probe shows the real server-side state instead of another occurrence of the same uninformative message. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 6db9f0f commit afcb4f1

2 files changed

Lines changed: 92 additions & 1 deletion

File tree

src/api/extraction/infrastructure/openshell/vertex_provider.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,39 @@ def ensure_vertex_provider(
136136
args.extend(["--config", f"VERTEX_AI_REGION={region.strip()}"])
137137

138138
with _home_for_adc(gcloud_config_mount=gcloud_config_mount):
139-
run_openshell(args, timeout=60.0)
139+
try:
140+
run_openshell(args, timeout=60.0)
141+
except OpenShellCliError as exc:
142+
diagnostic = _diagnose_vertex_profile()
143+
raise OpenShellCliError(
144+
f"{exc} | google-vertex-ai profile diagnostic: {diagnostic}"
145+
) from exc
140146
if auth_mode == "vertex":
141147
ensure_inference_routing(provider_name=provider_name, model=model)
142148

143149

150+
def _diagnose_vertex_profile() -> str:
151+
"""Dump the gateway's live google-vertex-ai profile for failure diagnostics.
152+
153+
OpenShell's ``--from-gcloud-adc`` emits an identical generic "no
154+
credentials resolved" error whether the ADC file is fine or the gateway's
155+
served provider profile doesn't allow empty-credential bootstrap (see
156+
OpenShell CLI ``missing_credentials_error`` / ``allows_empty_provider_credentials``).
157+
The two cases are indistinguishable from the CLI's own stderr, so capture
158+
the actual served profile at failure time instead of guessing.
159+
"""
160+
try:
161+
result = run_openshell(
162+
["provider", "profile", "export", "google-vertex-ai", "--output", "json"],
163+
check=False,
164+
timeout=15.0,
165+
)
166+
except OpenShellCliError as exc:
167+
return f"profile diagnostic call failed: {exc}"
168+
output = (result.stdout or result.stderr or "").strip()
169+
return output or "profile diagnostic returned no output"
170+
171+
144172
def ensure_inference_routing(*, provider_name: str, model: str) -> None:
145173
"""Point sandbox inference.local at the configured Vertex provider."""
146174
resolved_model = model.strip() or "claude-opus-4-6"

src/api/tests/unit/extraction/infrastructure/test_openshell_vertex_provider.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,69 @@ def test_home_for_adc_restores_previous_home(tmp_path) -> None:
140140
assert os.environ.get("HOME") == previous
141141

142142

143+
def test_ensure_vertex_provider_enriches_create_failure_with_profile_diagnostic(
144+
tmp_path,
145+
) -> None:
146+
"""On create failure, append a live `provider profile export` dump.
147+
148+
OpenShell's generic "no credentials resolved" error for google-vertex-ai
149+
hides whether the gateway's served profile actually allows empty-credential
150+
bootstrap (see openshell-cli run.rs `missing_credentials_error`). Since
151+
that can only be observed against the real gateway at failure time,
152+
surface it inline so it lands in the sticky-runtime probe's error field.
153+
"""
154+
adc_dir = tmp_path / "gcloud"
155+
adc_dir.mkdir()
156+
(adc_dir / "application_default_credentials.json").write_text(
157+
'{"type":"authorized_user"}', encoding="utf-8"
158+
)
159+
160+
import subprocess
161+
162+
diagnostic_result = subprocess.CompletedProcess(
163+
args=[],
164+
returncode=0,
165+
stdout='{"id": "google-vertex-ai", "credentials": []}',
166+
stderr="",
167+
)
168+
169+
with (
170+
patch(
171+
"extraction.infrastructure.openshell.vertex_provider.provider_exists",
172+
return_value=False,
173+
),
174+
patch(
175+
"extraction.infrastructure.openshell.vertex_provider.run_openshell",
176+
side_effect=[
177+
OpenShellCliError(
178+
"openshell provider create ... failed: no credentials resolved"
179+
),
180+
diagnostic_result,
181+
],
182+
) as run,
183+
):
184+
with pytest.raises(OpenShellCliError) as exc_info:
185+
ensure_vertex_provider(
186+
provider_name="kartograph-gma",
187+
project_id="my-project",
188+
region="us-east5",
189+
gcloud_config_mount=str(adc_dir),
190+
)
191+
192+
assert "no credentials resolved" in str(exc_info.value)
193+
assert '"id": "google-vertex-ai"' in str(exc_info.value)
194+
assert run.call_count == 2
195+
diagnostic_args = run.call_args_list[1].args[0]
196+
assert diagnostic_args == [
197+
"provider",
198+
"profile",
199+
"export",
200+
"google-vertex-ai",
201+
"--output",
202+
"json",
203+
]
204+
205+
143206
def test_ensure_vertex_provider_raises_when_adc_missing(tmp_path) -> None:
144207
with patch(
145208
"extraction.infrastructure.openshell.vertex_provider.provider_exists",

0 commit comments

Comments
 (0)