siyuan-note/siyuan · error

optionUpdate must name an existing option

Error message

optionUpdate must name an existing option

What it means

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.

Solutions

  1. Verify with the current option list that the name exists and send it exactly
  2. Use "options" (full list replace) or add the option first if creation was intended
  3. Match names exactly, including case; trim stray whitespace before lookup

Example fix

// before
{avID, keyID, setting: "optionUpdate", data: {name: "todo", newName: "In Progress"}}
// after
{avID, keyID, setting: "optionUpdate", data: {name: "Todo", newName: "In Progress"}}
Defensive patterns

Strategy: validation

Validate before calling

const opt = key.options?.find(o => o.name === name);
if (!opt) throw new Error(`cannot update missing option: ${name}`);

Try / catch

try { await updateOption(cfg); } catch (e) { if (String(e).includes("optionUpdate must name")) await reloadOptions(); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/599841ba6800c7a0. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/attribute_view_key_config.go:237

		name, ok := value.(string)
		if !ok || nil == key.GetOption(name) {
			return errors.New("optionRemove must name an existing option")
		}
		op.Data = name
		return removeAttributeViewColumnOption(op)
	}
	var config struct {
		Name    string  `json:"name"`
		NewName *string `json:"newName"`
		Color   *string `json:"color"`
		Desc    *string `json:"desc"`
	}
	if err := decodeAttributeViewKeyConfig(value, &config); nil != err {
		return err
	}
	option := key.GetOption(config.Name)
	if nil == option {
		return errors.New("optionUpdate must name an existing option")
	}
	name, color, desc := option.Name, option.Color, option.Desc
	if nil != config.NewName {
		name = strings.TrimSpace(*config.NewName)
		if "" == name {
			return errors.New("new option name must not be empty")
		}
		if name != option.Name && nil != key.GetOption(name) {
			return errors.New("new option name already exists")
		}
	}
	if nil != config.Color {
		color = *config.Color
		if attrView.FilterColorValue(color) != color {
			return errors.New("invalid option color")
		}
	}
	if nil != config.Desc {

View on GitHub (pinned to 9f775e8a12)