{"record":{"id":"8d3d5c07eab0c5dd","repo":"siyuan-note/siyuan","slug":"configuration-must-not-be-null","errorCode":null,"errorMessage":"configuration must not be null","messagePattern":"configuration must not be null","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_key_config.go","lineNumber":182,"sourceCode":"\t\t\t\tif av.FilterCombinationAnd != filter.Combination && av.FilterCombinationOr != filter.Combination {\n\t\t\t\t\treturn errors.New(\"filter group requires and/or combination\")\n\t\t\t\t}\n\t\t\t\tif err := validate(filter.Filters); nil != err {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else if _, err := dest.GetKey(filter.Column); nil != err {\n\t\t\t\treturn fmt.Errorf(\"invalid filter target field: %s\", filter.Column)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\treturn validate(filters)\n}\n\n// decodeAttributeViewKeyConfig 拒绝未知字段及空值，防止拼写错误被当作成功更新。\nfunc decodeAttributeViewKeyConfig(value, dest any) error {\n\tif nil == value {\n\t\treturn errors.New(\"configuration must not be null\")\n\t}\n\tdata, err := json.Marshal(value)\n\tif nil != err {\n\t\treturn err\n\t}\n\tdecoder := json.NewDecoder(bytes.NewReader(data))\n\tdecoder.DisallowUnknownFields()\n\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\")","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_key_config.go#L164-L200","documentation":"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.","triggerScenarios":"Calling UpdateAttributeViewKeyConfig (settings filters, rollupFilters, options, optionUpdate, relation, rollup) with value null, an empty body, or a request that omits the setting value entirely.","commonSituations":"API clients sending {\"setting\":\"options\"} without the payload field; frontend code passing undefined into the fetch wrapper; template-generated requests with unfilled value placeholders.","solutions":["Include a non-null configuration object/value in the request for the given setting","For 'options' pass an array of SelectOption objects; for 'optionRemove' pass a string; for others pass the documented config object","Inspect the request body actually sent (e.g. JSON.stringify result) to confirm it is not undefined/null"],"exampleFix":"// before\nfetchPost(\"/api/av/setAttributeViewKeyConfig\", {avID, keyID, setting: \"options\"})\n// after\nfetchPost(\"/api/av/setAttributeViewKeyConfig\", {avID, keyID, setting: \"options\", data: [{name: \"Todo\", color: \"1\"}]})","handlingStrategy":"type-guard","validationCode":"if (value === undefined || value === null) throw new Error(\"setAttributeViewKeyConfig requires a data payload\");","typeGuard":"const hasPayload = (v) => v !== undefined && v !== null;","tryCatchPattern":"try { await callApi(payload); } catch (e) { if (String(e).includes(\"must not be null\")) console.error(\"missing setting payload\", payload); }","preventionTips":["Never build request bodies conditionally without a default","Log the exact JSON body before sending when debugging key-config calls"],"tags":["api","validation","null"],"backgroundTag":"null-argument","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"}