siyuan-note/siyuan · error

configuration must not be null

Error message

configuration must not be null

What it means

decodeAttributeViewKeyConfig rejects a nil/absent configuration value before decoding. SiYuan's key-config update endpoints require an explicit JSON payload so a null can never be mistaken for a successful (no-op) update. It also uses DisallowUnknownFields to catch payload typos.

Solutions

  1. Include a non-null configuration object/value in the request for the given setting
  2. For 'options' pass an array of SelectOption objects; for 'optionRemove' pass a string; for others pass the documented config object
  3. Inspect the request body actually sent (e.g. JSON.stringify result) to confirm it is not undefined/null

Example fix

// before
fetchPost("/api/av/setAttributeViewKeyConfig", {avID, keyID, setting: "options"})
// after
fetchPost("/api/av/setAttributeViewKeyConfig", {avID, keyID, setting: "options", data: [{name: "Todo", color: "1"}]})
Defensive patterns

Strategy: type-guard

Validate before calling

if (value === undefined || value === null) throw new Error("setAttributeViewKeyConfig requires a data payload");

Type guard

const hasPayload = (v) => v !== undefined && v !== null;

Try / catch

try { await callApi(payload); } catch (e) { if (String(e).includes("must not be null")) console.error("missing setting payload", payload); }

Prevention

When it happens

Trigger: Calling UpdateAttributeViewKeyConfig (settings filters, rollupFilters, options, optionUpdate, relation, rollup) with value null, an empty body, or a request that omits the setting value entirely.

Common situations: API clients sending {"setting":"options"} without the payload field; frontend code passing undefined into the fetch wrapper; template-generated requests with unfilled value placeholders.

Related errors


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

Appendix: source

Thrown at kernel/model/attribute_view_key_config.go:182

				if av.FilterCombinationAnd != filter.Combination && av.FilterCombinationOr != filter.Combination {
					return errors.New("filter group requires and/or combination")
				}
				if err := validate(filter.Filters); nil != err {
					return err
				}
			} else if _, err := dest.GetKey(filter.Column); nil != err {
				return fmt.Errorf("invalid filter target field: %s", filter.Column)
			}
		}
		return nil
	}
	return validate(filters)
}

// decodeAttributeViewKeyConfig 拒绝未知字段及空值,防止拼写错误被当作成功更新。
func decodeAttributeViewKeyConfig(value, dest any) error {
	if nil == value {
		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")

View on GitHub (pinned to 9f775e8a12)