Skip to content

feat(cli): add --check flag to goose info for provider testing#8289

Merged
lifeizhou-ap merged 9 commits into
aaif-goose:mainfrom
mvanhorn:feat/info-provider-check
Apr 23, 2026
Merged

feat(cli): add --check flag to goose info for provider testing#8289
lifeizhou-ap merged 9 commits into
aaif-goose:mainfrom
mvanhorn:feat/info-provider-check

Conversation

@mvanhorn
Copy link
Copy Markdown
Contributor

@mvanhorn mvanhorn commented Apr 3, 2026

Summary

Adds a --check flag to goose info that tests the configured provider connection. Reports auth status, initialization time, and available model count. On failure, prints the error with a hint to run goose configure.

Demo

goose info --check demo

Three scenes: (1) goose info before this change (paths only), (2) goose info --check with no provider configured (warning + hint), (3) goose info --check with invalid credentials (auth failure correctly detected after credentialed API call).

Why this matters

Users discover broken provider configurations only after starting a session. The auth error appears after the session banner has already printed, and the error message gives no guidance on how to fix it. Several issues trace back to this first-use friction:

  • #6287 - GOOSE_DISABLE_KEYRING not working (4 reactions, 23 comments)
  • #6893 - CLI cannot use developer extension on fresh install (3 reactions)
  • #7903 - Azure AI 401 due to hardcoded Bearer auth
  • #8029 - Windows KeyRing makes goose unusable with MCP OAuth

This flag provides a quick pre-flight check without starting a full session.

Changes

Two files changed in crates/goose-cli/:

src/cli.rs (+2 lines): Added check: bool field to the Info command variant. Updated dispatch to pass check and .await the now-async handle_info.

src/commands/info.rs (+93 lines): Made handle_info async. When --check is passed, reads provider/model from Config::get_goose_provider()/get_goose_model(), creates the provider via goose::providers::create(), then calls fetch_supported_models() as the credentialed API call. Auth status is reported based on whether that call succeeds or returns a 401/auth error. Non-auth errors (network, rate limit) are reported separately under Models.

Three output paths:

  • No provider configured: prints warning with goose configure hint
  • Auth succeeds: shows auth ok, connection time, model count
  • Auth fails (401/Unauthorized): shows Auth: FAILED with actionable hint

Testing

  • cargo fmt --check passes
  • cargo clippy -p goose-cli --all-targets -- -D warnings passes (zero warnings)
  • cargo test -p goose-cli passes
  • Built from source and tested all three output paths manually (see demo gif above)

Related Issues

Relates to #6287, #6893, #7903, #8029

This contribution was developed with AI assistance (Codex + Claude Code).

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e61936675d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread crates/goose-cli/src/commands/info.rs Outdated
Comment on lines +111 to +114
"Auth:",
&style("ok").green().to_string(),
label_padding,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Delay auth success until a credentialed API call passes

This marks Auth: ok immediately after provider construction, before any authenticated request is made, so invalid credentials can still be reported as successful. For providers like OpenAI-compatible ones, construction can succeed without validating the key, and the first real auth check happens in fetch_supported_models(), which may then return 401 while the output still says auth is ok. Since --check is intended to validate provider setup, this is a false positive and can mislead users into thinking their auth is healthy when it is not.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52e7c8e72a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread crates/goose-cli/src/commands/info.rs Outdated
Comment on lines +160 to +164
print_aligned(
"Auth:",
&style("ok").green().to_string(),
label_padding,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Treat model-fetch errors as failed provider checks

When fetch_supported_models() returns an error that does not match the string checks for 401/Authentication/Unauthorized (for example NetworkError, RateLimitExceeded, or other request failures), this branch still prints Auth: ok (and immediately after, Connection: ok). That creates a false positive for goose info --check even though the verification call failed, so users can be told their provider is healthy during outages or connectivity failures.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 44e140d551

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread crates/goose-cli/src/commands/info.rs Outdated
Ok(model_config) => {
match goose::providers::create(&provider, model_config, Vec::new()).await {
Ok(p) => {
let elapsed = start.elapsed();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Measure verification latency after the API check runs

The Connection: ok (verified in …) value is computed before fetch_supported_models().await, so the reported verification time excludes the credentialed network call that actually validates connectivity. In slow or degraded networks this can print a near-zero "verified" duration even though the check took much longer, which makes the new health signal inaccurate for troubleshooting.

Useful? React with 👍 / 👎.

Comment thread crates/goose-cli/src/commands/info.rs Outdated
match goose::providers::create(&provider, model_config, Vec::new()).await {
Ok(p) => {
let elapsed = start.elapsed();
match p.fetch_supported_models().await {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this sounds like the right way to do it, since it is free for most providers, but it is not. for custom providers that specify their own models and have fetching disabled, this will always work. for openrouter, this will also always work. you should probably just send an actual test message

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call -- switched from fetch_supported_models() to a minimal complete() call (Message::user("Say 'ok'") with no tools). This actually exercises the auth path, so it'll catch bad keys on custom providers and OpenRouter where model listing always succeeds.

f83fc85

mvanhorn added a commit to mvanhorn/goose that referenced this pull request Apr 4, 2026
Replace fetch_supported_models() with a minimal completion call
(Message::user 'Say ok' with max 1 response). This catches auth
failures on custom providers with fetching disabled and on
OpenRouter, where model listing always succeeds regardless of
credentials.

Addresses review feedback from @DOsinga on PR aaif-goose#8289.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f83fc853f3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread crates/goose-cli/src/commands/info.rs Outdated
Comment on lines +199 to +205
_ => {
println!(
" {} No provider configured. Run '{}' first.",
style("⚠").yellow(),
style("goose configure").cyan()
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Surface config load errors instead of "No provider configured"

The fallback arm for match (provider_name, model_name) collapses every error into the same "No provider configured" message, so goose info --check misreports malformed/unreadable config files (or other retrieval failures) as missing setup. In those cases users are directed to reconfigure instead of seeing the real failure, which makes troubleshooting provider checks much harder. Please only use this message for actual NotFound cases and print other errors explicitly.

Useful? React with 👍 / 👎.

@mvanhorn
Copy link
Copy Markdown
Contributor Author

mvanhorn commented Apr 6, 2026

Addressed Codex findings in 818dacd:

  • [P2] Config load errors now surface the actual error message instead of generic "No provider configured"
  • [P1] Auth validation and [P2] latency measurement were already correct in the current implementation (auth OK only prints after the API call succeeds, timer wraps the complete() call)

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 818dacdd08

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread crates/goose-cli/src/commands/info.rs Outdated
print_aligned("Provider:", &provider, label_padding);
print_aligned("Model:", &model, label_padding);

match goose::model::ModelConfig::new(&model) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Apply canonical model limits before running --check

goose info --check builds the probe config with ModelConfig::new(&model) but never calls .with_canonical_limits(&provider), unlike the normal agent path. That leaves max_output_tokens at the fallback from ModelConfig::max_output_tokens() (4096), and provider request builders (e.g. OpenAI/Anthropic formatters) send that value directly; models with lower output caps then fail the probe even when auth and connectivity are valid. This creates false negatives for valid configurations and makes --check less reliable than an actual session.

Useful? React with 👍 / 👎.

@mvanhorn
Copy link
Copy Markdown
Contributor Author

mvanhorn commented Apr 7, 2026

Fixed in 5d76deb - added .with_canonical_limits(&provider) to the --check probe path so it matches the normal agent session behavior. Without this, models with lower output caps than the 4096 fallback would fail the probe even with valid auth.

Copy link
Copy Markdown
Collaborator

@DOsinga DOsinga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature is useful and addresses real user pain. A few things to fix:

1. Match on ProviderError variants instead of string-searching

complete() returns Result<_, ProviderError>, which already has structured variants like ProviderError::Authentication(_), ProviderError::NetworkError(_), etc. The current code does:

if err_str.contains("401")
    || err_str.contains("Authentication")
    || err_str.contains("Unauthorized")

This is brittle and will break if error messages change. Instead:

use goose::providers::errors::ProviderError;

match e {
    ProviderError::Authentication(_) => { /* auth failed */ }
    ProviderError::NetworkError(_) => { /* network issue */ }
    _ => { /* other failure */ }
}

2. Collapse the duplicated config-error arms

The (Err(e), _) and (_, Err(e)) branches do the same thing. You can combine them:

(Ok(provider), Ok(model)) => { /* ... */ }
_ => {
    // print whichever is missing
}

Or even simpler — just try to get both and bail on the first error, since you can't do anything useful without both.

3. Flatten the nesting

The happy path is 5+ levels deep. Consider extracting the provider-check logic into a helper function that returns a Result, then match on that result at the top level for display. This would make the output formatting much easier to follow.

@DOsinga DOsinga added the needs_human label to set when a robot looks at a PR and can't handle it label Apr 8, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 97f28e92fe

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread crates/goose-cli/src/commands/info.rs Outdated
Comment thread crates/goose-cli/src/commands/info.rs Outdated
@mvanhorn
Copy link
Copy Markdown
Contributor Author

mvanhorn commented Apr 8, 2026

Addressed all three items in 97f28e9:

  1. Replaced string matching with structured match on ProviderError variants
  2. Collapsed the duplicated config-error arms into a shared NotConfigured path
  3. Extracted the check logic into a check_provider helper to flatten the nesting

cargo check passes. User-facing output is unchanged.

@lifeizhou-ap
Copy link
Copy Markdown
Collaborator

lifeizhou-ap commented Apr 22, 2026

Hi @mvanhorn,

Thanks for the careful iteration here.

The refactor into check_provider with typed ProviderError matching reads really well.

Two Codex comments from the last pass still look open: returning a non-zero exit code when the check fails (P1), and separating provider-init errors from auth failures (P2)

could you address those (or reply why they don't apply) and rebase onto main? Happy to take another look after that.

@lifeizhou-ap lifeizhou-ap self-assigned this Apr 22, 2026
mvanhorn added a commit to mvanhorn/goose that referenced this pull request Apr 22, 2026
…n check failure

Addresses the two open Codex items @lifeizhou-ap flagged on aaif-goose#8289:

- Split `ProviderCheckError::ProviderCreate`: when `show_api_key_hint`
  is true the failure is reported as `Auth: FAILED` (missing or invalid
  credential), otherwise as `Provider: FAILED`. The previous code
  blanket-labeled every construction failure as auth, which misdirected
  troubleshooting when the real issue was an unknown provider or
  malformed provider config.
- Return `Err(anyhow!(...))` from `handle_info` when the check fails so
  `goose info --check` exits non-zero. Automation (CI scripts, install
  checks, health probes) can now rely on the exit code.

Verified: cargo check, cargo clippy --deny warnings, and
cargo test -p goose-cli --lib (163 pass).
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db1ec06e80

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

let model_config = provider_client.get_model_config();
let start = std::time::Instant::now();
provider_client
.complete(&model_config, "check", "", &[test_msg], &[])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use a unique session ID for provider probe requests

Passing the hard-coded session id "check" causes every goose info --check probe to present the same user/session identity to providers. In openrouter::stream, session_id is copied into the user field specifically for attribution/rate-limiting, so this change can create cross-user throttling and intermittent false failures even when an individual user’s credentials are valid. Generating a per-invocation id (or leaving it empty for a pre-session probe) avoids this shared-rate-limit coupling.

Useful? React with 👍 / 👎.

@mvanhorn
Copy link
Copy Markdown
Contributor Author

Addressed both items in db1ec06:

[P1] Return non-zero status on check failure

handle_info now stores the result and returns Err(anyhow!("provider check failed")) after the error is printed. goose info --check exits non-zero when any of the configured/model/provider/auth/request branches fail, so CI scripts and health probes can use the exit code as a pre-flight signal.

[P2] Separate provider-init errors from auth failures

ProviderCheckError::ProviderCreate now branches on show_api_key_hint: when true (error mentions API_KEY / not found) it reports Auth: FAILED with the configure hint; otherwise it reports Provider: FAILED with a hint to check the provider name and config. Unknown providers / malformed configs no longer misdirect users toward rotating keys.

Verified: cargo check -p goose-cli, cargo clippy -p goose-cli --deny warnings, and cargo test -p goose-cli --lib (163 pass).

@lifeizhou-ap
Copy link
Copy Markdown
Collaborator

Hi @mvanhorn Thank you so much for the quick reply! There are some merge conflicts, would you please resolve it?

mvanhorn and others added 9 commits April 22, 2026 18:59
…ting

Adds a --check flag to `goose info` that tests the configured provider
connection and reports auth status, initialization time, and available
model count. On failure, prints the error with an actionable hint.

Users currently discover broken provider configurations only after
starting a session. This flag lets them verify setup works before
committing to a session.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
The initial implementation used config.get_param("provider") which
reads from the PROVIDER env var. The correct accessors are
get_goose_provider() and get_goose_model() which read from
GOOSE_PROVIDER/GOOSE_MODEL and the config file.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Addresses Codex review feedback: provider construction can succeed
without validating credentials, so Auth: ok was a false positive.
Now fetch_supported_models() runs first, and auth status reflects
whether the credentialed call actually passed.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Non-auth errors (network, rate limit) from fetch_supported_models()
were still printing Auth: ok. Now any verification failure shows
Check: FAILED with the error message.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Replace fetch_supported_models() with a minimal completion call
(Message::user 'Say ok' with max 1 response). This catches auth
failures on custom providers with fetching disabled and on
OpenRouter, where model listing always succeeds regardless of
credentials.

Addresses review feedback from @DOsinga on PR aaif-goose#8289.
Previously the catch-all arm showed a generic "No provider configured"
message. Now it reports the actual config error so users can diagnose
whether the provider or model key is missing or malformed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ModelConfig::new() alone uses a fallback max_output_tokens (4096) which
can exceed the actual model's limit, causing false probe failures. Apply
with_canonical_limits() to match the normal agent session path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract provider-check logic into a helper function, replace brittle
string searching with match on ProviderError variants, and collapse
duplicated config-error branches. Keeps user-facing output unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n check failure

Addresses the two open Codex items @lifeizhou-ap flagged on aaif-goose#8289:

- Split `ProviderCheckError::ProviderCreate`: when `show_api_key_hint`
  is true the failure is reported as `Auth: FAILED` (missing or invalid
  credential), otherwise as `Provider: FAILED`. The previous code
  blanket-labeled every construction failure as auth, which misdirected
  troubleshooting when the real issue was an unknown provider or
  malformed provider config.
- Return `Err(anyhow!(...))` from `handle_info` when the check fails so
  `goose info --check` exits non-zero. Automation (CI scripts, install
  checks, health probes) can now rely on the exit code.

Verified: cargo check, cargo clippy --deny warnings, and
cargo test -p goose-cli --lib (163 pass).

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
@mvanhorn mvanhorn force-pushed the feat/info-provider-check branch from db1ec06 to 9784419 Compare April 23, 2026 02:02
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9784419171

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

.map_err(|e| {
let error = e.to_string();
ProviderCheckError::ProviderCreate {
show_api_key_hint: error.contains("not found") || error.contains("API_KEY"),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Narrow auth-hint detection to credential-specific errors

The show_api_key_hint heuristic treats any provider-create error containing "not found" as an auth failure, which misclassifies non-credential setup errors as Auth: FAILED and gives the wrong remediation. Fresh evidence: Azure provider construction reads required non-auth fields (AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME) via get_param in crates/goose/src/providers/azure.rs lines 78-79, so missing endpoint/deployment currently triggers this auth branch even when API keys are fine. This makes goose info --check diagnostics misleading and should be restricted to explicit credential-key failures.

Useful? React with 👍 / 👎.

@mvanhorn
Copy link
Copy Markdown
Contributor Author

Rebased onto main and force-pushed. Two conflicts: crates/goose-cli/src/cli.rs (your new Doctor command + my Info { verbose, check } change merged cleanly), and Cargo.lock (took the upstream goose-sdk 1.31.0 bump). cargo fmt, cargo clippy --all-targets -- -D warnings, and cargo test -p goose-cli --lib (191 tests) all pass locally.

@lifeizhou-ap lifeizhou-ap added this pull request to the merge queue Apr 23, 2026
Merged via the queue into aaif-goose:main with commit 5f3c63a Apr 23, 2026
21 checks passed
@mvanhorn
Copy link
Copy Markdown
Contributor Author

Appreciate getting --check on goose info, @lifeizhou-ap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs_human label to set when a robot looks at a PR and can't handle it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants