fix(config): unbrick first-run setup — default google/anthropic models, enter setup on fixable config errors#385
Conversation
A hand-written provider entry like {name: "google", provider_kind:
"google", apiKey: ...} with no model bricked Zero: config resolution
failed with "provider google requires model", and since zero config and
bare zero setup both resolve up front, the only commands that could have
fixed the config died with the same error (reported in the wild — the
one working escape, zero setup google --model <m>, is undiscoverable
from that message).
The openai kind has always defaulted a missing model
(modelregistry.DefaultModelID); google and anthropic were missed. Fall
back to their provider-catalog default models (gemini-2.5-pro /
claude-sonnet-4.5) the same way, behind the same gate: provider-command
configs (normalizeProvidersWithoutModelDefaults) still surface exactly
what the external command returned. An explicitly configured model
always wins, and profiles with a catalogID already got this via
applyCatalogDescriptor — this covers the catalog-less hand-written case.
The residual requires-model error (openai-compatible customs, which
have no catalog default) now carries an actionable hint: add "model" in
config.json or re-run zero setup with --model.
Tests: google/anthropic defaulting (red-green: reverting the google
fallback reproduces the exact reported error), explicit model wins,
provider-command gate still errors with the hint, and validation treats
official-kind model-less profiles as clean while still flagging customs.
Zero automated PR reviewVerdict: No blockers found Blockers
Validation
ScopeHead: This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality. |
WalkthroughProvider model resolution now uses catalog defaults for official providers, returns a setup-fixable missing-model error, and the CLI adds login/logout suggestions while treating missing-model startup failures as interactive-setup recoverable. ChangesProvider model defaults and CLI recovery
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant runWithDeps
participant runInteractiveTUIWithSetup
participant configResolve
participant SetupWizard
runWithDeps->>runWithDeps: handle unknown login/logout suggestion
runInteractiveTUIWithSetup->>configResolve: resolve active provider config
configResolve-->>runInteractiveTUIWithSetup: ErrProviderRequiresModel
runInteractiveTUIWithSetup->>SetupWizard: launch interactive setup
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
…ogin Two residual first-run gaps after the catalog model-defaulting fix: - An active provider whose model has NO catalog default to fall back on (custom openai-/anthropic-compatible endpoints) still bricked bare `zero` and `zero setup` — the exact commands that could have repaired the config. Tag that resolve failure with ErrProviderRequiresModel and extend the interactive TUI's fixable-error fallback (previously only ErrNoActiveProvider) to drop into the setup wizard for it. The sentinel is attached via a message-preserving wrapper (multi-target Unwrap), so headless commands (zero config / zero exec) print the exact actionable text unchanged and every other resolve error stays fatal. - `zero login` / `zero logout` don't exist (it's `zero auth login`); first-run users try them and got only "unknown command" (reported in the wild). The unknown-command error now answers: did you mean "zero auth login"? Unrelated unknown commands get no suggestion. Tests: the sentinel comes from a REAL Resolve over a real config file (message-prefix asserted, no key leak); the TUI fallback is red-green (reverting the condition reproduces the exact brick with the old error); the existing no-active-provider fallback and other-errors-stay-fatal tests still pass; login/logout hint with a no-false-positive check.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/cli/app.go (1)
541-556: 🩺 Stability & Availability | 🔵 TrivialFull resolve failure loses saved-provider fallback (pre-existing, now reachable via a second path).
When Resolve fails for the active provider, resolver.go's
normalizeProvidersWithOptionsdrops the entire provider list, not just the broken active entry. So even if the user has another valid saved provider,firstUsableProviderfallback (line 582) never gets a chance to run here sinceforceSetupis alreadytrue. This limitation pre-dates this PR (already true forErrNoActiveProvider) but is now also triggered byErrProviderRequiresModel.Not blocking this PR, but consider whether
Resolvecould preserve the other valid providers even when the active one fails, so users with multiple saved providers aren't forced into full re-onboarding.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/app.go` around lines 541 - 556, Resolve currently drops the whole provider list when the active provider fails, which prevents saved-provider fallback from being used in the CLI flow. Review `Resolve` and `normalizeProvidersWithOptions` so a bad active provider only removes or marks that entry while preserving other valid saved providers, allowing `firstUsableProvider` to still pick one before `forceSetup` is set. Keep the existing fatal handling for unrecoverable errors, but make the provider normalization path in `config.ResolvedConfig` resilient to a broken active provider.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@internal/cli/app.go`:
- Around line 541-556: Resolve currently drops the whole provider list when the
active provider fails, which prevents saved-provider fallback from being used in
the CLI flow. Review `Resolve` and `normalizeProvidersWithOptions` so a bad
active provider only removes or marks that entry while preserving other valid
saved providers, allowing `firstUsableProvider` to still pick one before
`forceSetup` is set. Keep the existing fatal handling for unrecoverable errors,
but make the provider normalization path in `config.ResolvedConfig` resilient to
a broken active provider.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0b21dd91-cc2e-4a58-9904-6caa99049ac6
📒 Files selected for processing (4)
internal/cli/app.gointernal/cli/app_test.gointernal/config/resolver.gointernal/config/resolver_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/config/resolver.go
anandh8x
left a comment
There was a problem hiding this comment.
Clean, well-tested fix. Two things done right:
- Catalog defaults for google/anthropic — mirrors the existing openai pattern. Explicit model wins, provider-command path untouched.
setupFixableError— multi-targetUnwrap()is the right pattern for tagging without polluting the user-facing message. The TUI fallback and headless hint cover both paths cleanly.
Tests cover defaulting, explicit-model-wins, provider-command gate, sentinel wrapping, key-leak guard, validation, TUI fallback, login hint, and false-positive. No gaps.
LGTM.
…s, enter setup on fixable config errors (Gitlawb#385) * fix(config): default missing google/anthropic models from the catalog A hand-written provider entry like {name: "google", provider_kind: "google", apiKey: ...} with no model bricked Zero: config resolution failed with "provider google requires model", and since zero config and bare zero setup both resolve up front, the only commands that could have fixed the config died with the same error (reported in the wild — the one working escape, zero setup google --model <m>, is undiscoverable from that message). The openai kind has always defaulted a missing model (modelregistry.DefaultModelID); google and anthropic were missed. Fall back to their provider-catalog default models (gemini-2.5-pro / claude-sonnet-4.5) the same way, behind the same gate: provider-command configs (normalizeProvidersWithoutModelDefaults) still surface exactly what the external command returned. An explicitly configured model always wins, and profiles with a catalogID already got this via applyCatalogDescriptor — this covers the catalog-less hand-written case. The residual requires-model error (openai-compatible customs, which have no catalog default) now carries an actionable hint: add "model" in config.json or re-run zero setup with --model. Tests: google/anthropic defaulting (red-green: reverting the google fallback reproduces the exact reported error), explicit model wins, provider-command gate still errors with the hint, and validation treats official-kind model-less profiles as clean while still flagging customs. * fix(cli): enter setup on a requires-model config and hint zero auth login Two residual first-run gaps after the catalog model-defaulting fix: - An active provider whose model has NO catalog default to fall back on (custom openai-/anthropic-compatible endpoints) still bricked bare `zero` and `zero setup` — the exact commands that could have repaired the config. Tag that resolve failure with ErrProviderRequiresModel and extend the interactive TUI's fixable-error fallback (previously only ErrNoActiveProvider) to drop into the setup wizard for it. The sentinel is attached via a message-preserving wrapper (multi-target Unwrap), so headless commands (zero config / zero exec) print the exact actionable text unchanged and every other resolve error stays fatal. - `zero login` / `zero logout` don't exist (it's `zero auth login`); first-run users try them and got only "unknown command" (reported in the wild). The unknown-command error now answers: did you mean "zero auth login"? Unrelated unknown commands get no suggestion. Tests: the sentinel comes from a REAL Resolve over a real config file (message-prefix asserted, no key leak); the TUI fallback is red-green (reverting the condition reproduces the exact brick with the old error); the existing no-active-provider fallback and other-errors-stay-fatal tests still pass; login/logout hint with a no-false-positive check.
Fixes #384.
Problem
A provider entry with an API key but no
modelbricked Zero — reported twice in the wild against a freshnpm install -g(#384):Chicken-and-egg:
zero configand barezero setupresolve the full config up front, so the exact commands that could fix the config died with the same error. The trigger doesn't even need a config file —applyEnvsynthesizes a google provider from aGEMINI_API_KEY/GOOGLE_API_KEYexported in the user's shell, with no model unlessGEMINI_MODELis also set, which is how a completely fresh install hits this. Anthropic had the identical gap.Fix (commit 1): default missing google/anthropic models from the catalog
The resolver has always defaulted a missing model for the openai kind, and
catalogID-carrying profiles get defaults viaapplyCatalogDescriptor— but catalog-lessprovider_kind: google/anthropicprofiles (hand-written or env-synthesized) had no fallback. They now fall back to their provider-catalog default models (gemini-2.5-pro/claude-sonnet-4.5), same shape and same gate as openai (option renameddefaultOpenAIModel→defaultModels). An explicit model always wins; provider-command configs (ZERO_PROVIDER_COMMAND) still get no invented models (pinned by a test).Fix (commit 2): enter setup on the remaining fixable shape +
zero loginhintzero/zero setup. The failure is now taggedErrProviderRequiresModelvia a message-preserving wrapper (multi-targetUnwrap), and the interactive TUI's fixable-error fallback (previouslyErrNoActiveProvideronly, from fix(config): create the right config file on first setup (macOS ~/.config) (#371) #372) also drops into the setup wizard for it. Headless commands (zero config,zero exec) still fail with the actionable message, now with a hint:provider gw requires model — add "model" to its entry in config.json, or re-run: zero setup <catalog-id> --model <model>. Every other resolve error (malformed JSON, directory conflicts) stays fatal.zero login/zero logoutdon't exist (it'szero auth login); first-run users try them (Bug: First-run setup is blocked by "provider google requires model" before setup/auth can run #384). The unknown-command error now answersdid you mean "zero auth login"?— unrelated unknown commands get no suggestion.Testing
Resolveover a real config file (not a hand-built error); red-green — reverting the condition reproduces the exact brick. Existing no-active-provider fallback and other-errors-stay-fatal tests unchanged and passing.zero,zero setup,zero configwith onlyGEMINI_API_KEYexported) now works; the custom-endpoint shape enters the wizard interactively and errors actionably headless;zero loginprints the suggestion.go test ./...— 73/73 packages pass.Summary by CodeRabbit
auth login/auth logoutcommands for common typos.