{"record":{"id":"7ce8f28f0a5a2244","repo":"siyuan-note/siyuan","slug":"duplicate-option-name","errorCode":null,"errorMessage":"duplicate option name","messagePattern":"duplicate option name","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_key_config.go","lineNumber":208,"sourceCode":"\treturn decoder.Decode(dest)\n}\n\nfunc updateAttributeViewKeyOptions(attrView *av.AttributeView, key *av.Key, op *Operation, setting string, value any) error {\n\tif \"options\" == setting {\n\t\tvar options []*av.SelectOption\n\t\tif err := decodeAttributeViewKeyConfig(value, &options); nil != err {\n\t\t\treturn err\n\t\t}\n\t\tif 0 == len(options) {\n\t\t\treturn errors.New(\"options must not be empty; use optionRemove to delete existing options\")\n\t\t}\n\t\tnames := map[string]bool{}\n\t\tfor _, option := range options {\n\t\t\tif nil == option || \"\" == strings.TrimSpace(option.Name) {\n\t\t\t\treturn errors.New(\"each option requires a nonempty name\")\n\t\t\t}\n\t\t\tif names[option.Name] {\n\t\t\t\treturn errors.New(\"duplicate option name\")\n\t\t\t}\n\t\t\tif attrView.FilterColorValue(option.Color) != option.Color {\n\t\t\t\treturn errors.New(\"invalid option color\")\n\t\t\t}\n\t\t\tnames[option.Name] = true\n\t\t}\n\t\top.Data = options\n\t\treturn updateAttributeViewColumnOptions(op)\n\t}\n\tif \"optionRemove\" == setting {\n\t\tname, ok := value.(string)\n\t\tif !ok || nil == key.GetOption(name) {\n\t\t\treturn errors.New(\"optionRemove must name an existing option\")\n\t\t}\n\t\top.Data = name\n\t\treturn removeAttributeViewColumnOption(op)\n\t}\n\tvar config struct {","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_key_config.go#L190-L226","documentation":"The 'options' setting replaces the whole option list, and option names must be unique within a select/mselect field because they are the cell value identity. A submitted list containing the same name twice is rejected.","triggerScenarios":"Calling UpdateAttributeViewKeyConfig with setting \"options\" where two entries share the same Name string (exact match, after no normalization other than the non-empty check).","commonSituations":"Merging option lists from two sources without deduplication; duplicating an entry when editing the payload by hand; case-variant duplicates like \"todo\" and \"Todo\" are distinct here but visually confusing, while exact duplicates fail.","solutions":["Deduplicate the option array by Name before sending","Send one entry per option; update metadata of an existing option via \"optionUpdate\" instead of adding a second entry","Normalize names (trim) before the uniqueness check client-side"],"exampleFix":"// before\n[{\"name\":\"Todo\"},{\"name\":\"Todo\",\"color\":\"2\"}]\n// after\n[{\"name\":\"Todo\",\"color\":\"2\"}]","handlingStrategy":"validation","validationCode":"const names = options.map(o => o.name);\nif (new Set(names).size !== names.length) throw new Error(\"duplicate option names in payload\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Deduplicate by name before sending the options list","Keep a single source of truth for the option list instead of merging arrays ad hoc"],"tags":["attribute-view","select-options","uniqueness"],"backgroundTag":"conflicting-config-options","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}