{"record":{"id":"f18299ef48e86176","repo":"alibaba/open-code-review","slug":"model-name-must-not-be-empty","errorCode":null,"errorMessage":"model name must not be empty","messagePattern":"model name must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/opencodereview/provider_tui.go","lineNumber":816,"sourceCode":"\t\tm.refreshModelSelectionForCustom()\n\t\treturn m, nil\n\tdefault:\n\t\tvar cmd tea.Cmd\n\t\tm.modelInput, cmd = m.modelInput.Update(msg)\n\t\tm.formError = \"\"\n\t\treturn m, cmd\n\t}\n}\n\n// persistCustomModelName appends a single model name to the active provider's\n// Models list (official or custom) and saves the config. It does not change\n// the active model — the user picks that explicitly from the list afterwards.\n//\n// Returns (persisted, error). When no provider is active (neither official\n// nor custom), persisted is false and the caller decides how to handle it.\nfunc (m *providerTUIModel) persistCustomModelName(name string) (bool, error) {\n\tif name == \"\" {\n\t\treturn false, fmt.Errorf(\"model name must not be empty\")\n\t}\n\tif m.existingCfg == nil {\n\t\treturn false, nil\n\t}\n\tswitch m.activeTab {\n\tcase tabCustom:\n\t\tcp, ok := m.selectedCustomProvider()\n\t\tif !ok {\n\t\t\treturn false, nil\n\t\t}\n\t\tentry := m.customProviderEntry(cp.name, cp.entry)\n\t\tprevEntry := cloneProviderEntry(entry)\n\t\tentry.Models = append(entry.Models, name)\n\t\tif m.existingCfg.CustomProviders == nil {\n\t\t\tm.existingCfg.CustomProviders = make(map[string]ProviderEntry)\n\t\t}\n\t\tm.existingCfg.CustomProviders[cp.name] = entry\n\t\tcp.entry = entry","sourceCodeStart":798,"sourceCodeEnd":834,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/cmd/opencodereview/provider_tui.go#L798-L834","documentation":"providerTUIModel.persistCustomModelName appends a model name to the active provider's Models list and saves the config; it rejects an empty name up front with this error. It prevents creating empty entries in the provider's model list in the config file.","triggerScenarios":"Calling persistCustomModelName with name == \"\" — e.g. the TUI's add-model input confirmed with no text typed, or a caller passing an empty string programmatically.","commonSituations":"Pressing Enter on an empty add-model prompt in the provider TUI; whitespace-only input trimmed to empty; automation driving the TUI state with a blank name.","solutions":["Type a non-empty model name before confirming in the add-model input","Ignore/handle the (false, error) return for blank input instead of surfacing it as a hard failure — this is a normal no-op case for blank input","Trim whitespace in the caller and skip saving when the result is empty"],"exampleFix":"// before: caller surfaces blank-input error\npersisted, err := m.persistCustomModelName(input)\nif err != nil { m.err = err }\n// after: skip blank input\nif strings.TrimSpace(input) == \"\" { return }\npersisted, err := m.persistCustomModelName(strings.TrimSpace(input))","handlingStrategy":"validation","validationCode":"name := strings.TrimSpace(input)\nif name == \"\" { return } // skip blank add-model input before persisting","typeGuard":"func isNonEmptyModelName(s string) bool { return strings.TrimSpace(s) != \"\" }","tryCatchPattern":"persisted, err := m.persistCustomModelName(name)\nif err != nil {\n    if strings.Contains(err.Error(), \"model name must not be empty\") {\n        return false, nil // benign blank-input case, don't surface as failure\n    }\n    return persisted, err\n}","preventionTips":["Disable the confirm action when the add-model input is empty","Trim whitespace from input before persisting","Treat blank input as a no-op rather than an error in UI flows"],"tags":["validation","tui","model-list"],"backgroundTag":"empty-required-value","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}