{"record":{"id":"599841ba6800c7a0","repo":"siyuan-note/siyuan","slug":"optionupdate-must-name-an-existing-option","errorCode":null,"errorMessage":"optionUpdate must name an existing option","messagePattern":"optionUpdate must name an existing option","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_key_config.go","lineNumber":237,"sourceCode":"\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 {\n\t\tName    string  `json:\"name\"`\n\t\tNewName *string `json:\"newName\"`\n\t\tColor   *string `json:\"color\"`\n\t\tDesc    *string `json:\"desc\"`\n\t}\n\tif err := decodeAttributeViewKeyConfig(value, &config); nil != err {\n\t\treturn err\n\t}\n\toption := key.GetOption(config.Name)\n\tif nil == option {\n\t\treturn errors.New(\"optionUpdate must name an existing option\")\n\t}\n\tname, color, desc := option.Name, option.Color, option.Desc\n\tif nil != config.NewName {\n\t\tname = strings.TrimSpace(*config.NewName)\n\t\tif \"\" == name {\n\t\t\treturn errors.New(\"new option name must not be empty\")\n\t\t}\n\t\tif name != option.Name && nil != key.GetOption(name) {\n\t\t\treturn errors.New(\"new option name already exists\")\n\t\t}\n\t}\n\tif nil != config.Color {\n\t\tcolor = *config.Color\n\t\tif attrView.FilterColorValue(color) != color {\n\t\t\treturn errors.New(\"invalid option color\")\n\t\t}\n\t}\n\tif nil != config.Desc {","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_key_config.go#L219-L255","documentation":"The 'optionUpdate' path decodes a {name, newName, color, desc} config and looks up the existing option by config.Name. If no option with that exact name exists the update is rejected, because optionUpdate edits in place rather than creating.","triggerScenarios":"Calling UpdateAttributeViewKeyConfig with setting \"optionUpdate\" (or any setting that falls through to the update branch) where Name is empty, misspelled, refers to an option deleted earlier, or differs in case from the stored name.","commonSituations":"Plugin updating an option after the user renamed it in the UI; client confusing option Name with option ID; whitespace in the submitted name that is not in the stored one.","solutions":["Verify with the current option list that the name exists and send it exactly","Use \"options\" (full list replace) or add the option first if creation was intended","Match names exactly, including case; trim stray whitespace before lookup"],"exampleFix":"// before\n{avID, keyID, setting: \"optionUpdate\", data: {name: \"todo\", newName: \"In Progress\"}}\n// after\n{avID, keyID, setting: \"optionUpdate\", data: {name: \"Todo\", newName: \"In Progress\"}}","handlingStrategy":"validation","validationCode":"const opt = key.options?.find(o => o.name === name);\nif (!opt) throw new Error(`cannot update missing option: ${name}`);","typeGuard":null,"tryCatchPattern":"try { await updateOption(cfg); } catch (e) { if (String(e).includes(\"optionUpdate must name\")) await reloadOptions(); }","preventionTips":["Re-read the option list before each update; do not cache names across edits","Match names exactly, including case"],"tags":["attribute-view","select-options","api"],"backgroundTag":"record-not-found","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"}