{"record":{"id":"cfeccca14fedf161","repo":"alibaba/open-code-review","slug":"provider-name-is-required","errorCode":null,"errorMessage":"provider name is required","messagePattern":"provider name is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/opencodereview/provider_cmd.go","lineNumber":159,"sourceCode":"\n\tfmt.Println(\"\\nManual configuration saved.\")\n\tfmt.Printf(\"URL: %s\\n\", result.url)\n\tfmt.Printf(\"Protocol: %s\\n\", protocol)\n\tfmt.Printf(\"Model: %s\\n\", result.model)\n\n\tfmt.Println(\"\\nTesting connection...\")\n\tif err := runLLMTest(); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"Connection test failed: %v\\n\", err)\n\t\tfmt.Fprintln(os.Stderr, \"Configuration has been saved. Fix the issue and run 'ocr llm test' to re-verify.\")\n\t\treturn nil\n\t}\n\n\treturn nil\n}\n\nfunc applyCustomProviderConfig(configPath string, cfg *Config, result providerTUIResult) error {\n\tif result.provider == \"\" {\n\t\treturn fmt.Errorf(\"provider name is required\")\n\t}\n\tmodel := result.resolvedModel()\n\tif model == \"\" {\n\t\treturn fmt.Errorf(\"model is required\")\n\t}\n\n\tif cfg.CustomProviders == nil {\n\t\tcfg.CustomProviders = make(map[string]ProviderEntry)\n\t}\n\n\tentry := cfg.CustomProviders[result.provider]\n\tentry.Model = model\n\tif len(result.models) > 0 {\n\t\tentry.Models = append([]string(nil), result.models...)\n\t}\n\tentry.Models = ensureModelInList(entry.Models, model)\n\tif result.url != \"\" {\n\t\tentry.URL = result.url","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/cmd/opencodereview/provider_cmd.go#L141-L177","documentation":"applyCustomProviderConfig is the step of 'ocr config provider' that persists a custom (non-preset) provider entry chosen in the TUI wizard. It rejects the write when the wizard result carries no provider name, since a config entry cannot be keyed without one. The check guards result.provider, which the TUI flow is expected to populate.","triggerScenarios":"Running the provider config flow where providerTUIResult.provider is the empty string when applyCustomProviderConfig is invoked (e.g. the TUI was skipped, cancelled, or produced an empty selection); exercised by TestApplyCustomProviderConfig_MissingProvider.","commonSituations":"Cancelling out of the provider wizard so the selection struct stays zero-valued; a scripted/non-interactive path constructing providerTUIResult manually and forgetting to set Provider; refactors that reorder wizard fields so the provider pick is lost.","solutions":["Set the provider name in the TUI result (result.provider) before calling applyCustomProviderConfig","Ensure the provider wizard completes selection rather than returning early on cancel; exit the command cleanly instead","If constructing providerTUIResult programmatically, pass the provider name explicitly"],"exampleFix":"// before\nresult := providerTUIResult{} // provider empty\nerr := applyCustomProviderConfig(path, cfg, result)\n// after\nresult := providerTUIResult{provider: \"my-llm\", model: \"model-a\"}\nerr := applyCustomProviderConfig(path, cfg, result)","handlingStrategy":"validation","validationCode":"if result.provider == \"\" {\n    return errors.New(\"select a provider before saving configuration\")\n}\n// safe to call applyCustomProviderConfig(configPath, cfg, result)","typeGuard":"func hasProvider(r providerTUIResult) bool { return r.provider != \"\" }","tryCatchPattern":null,"preventionTips":["Always complete the wizard selection; on cancel, exit the command instead of applying","Assert provider non-empty in tests that exercise the apply path","Treat providerTUIResult.provider as a required field when constructing it programmatically"],"tags":["config","cli","validation","provider"],"backgroundTag":"missing-required-argument","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}