siyuan-note/siyuan · error

options must not be empty; use optionRemove to delete…

Error message

options must not be empty; use optionRemove to delete existing options

What it means

When replacing the option list of a select/mselect field ('options' setting), the new list must contain at least one option. SiYuan deliberately forbids clearing all options through this path and directs you to the 'optionRemove' setting, which safely removes a single existing option and handles rows that reference it.

Solutions

  1. Send the full desired non-empty option list instead of an empty array
  2. Use setting "optionRemove" with the option's name to delete a single existing option
  3. If the field truly must have no options, delete the field and recreate it, or add at least one placeholder option

Example fix

// before
{avID, keyID, setting: "options", data: []}
// after
{avID, keyID, setting: "optionRemove", data: "Unused Option"}
Defensive patterns

Strategy: validation

Validate before calling

if (setting === "options" && (!Array.isArray(options) || options.length === 0)) {
  throw new Error("use optionRemove to delete an option, cannot send empty options list");
}

Type guard

const canReplaceOptions = (opts) => Array.isArray(opts) && opts.length > 0;

Prevention

When it happens

Trigger: Calling UpdateAttributeViewKeyConfig with setting "options" and an empty array (or null-length decode result) for a select/multi-select key.

Common situations: Frontend sends the current option list minus the one the user deleted, leaving zero items; bulk-import code writes an empty array for fields with no options yet; template that resets a field's options to [].

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/attribute_view_key_config.go:200

		return errors.New("configuration must not be null")
	}
	data, err := json.Marshal(value)
	if nil != err {
		return err
	}
	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	return decoder.Decode(dest)
}

func updateAttributeViewKeyOptions(attrView *av.AttributeView, key *av.Key, op *Operation, setting string, value any) error {
	if "options" == setting {
		var options []*av.SelectOption
		if err := decodeAttributeViewKeyConfig(value, &options); nil != err {
			return err
		}
		if 0 == len(options) {
			return errors.New("options must not be empty; use optionRemove to delete existing options")
		}
		names := map[string]bool{}
		for _, option := range options {
			if nil == option || "" == strings.TrimSpace(option.Name) {
				return errors.New("each option requires a nonempty name")
			}
			if names[option.Name] {
				return errors.New("duplicate option name")
			}
			if attrView.FilterColorValue(option.Color) != option.Color {
				return errors.New("invalid option color")
			}
			names[option.Name] = true
		}
		op.Data = options
		return updateAttributeViewColumnOptions(op)
	}
	if "optionRemove" == setting {

View on GitHub (pinned to 9f775e8a12)