{"record":{"id":"79f37d4d1c5760c2","repo":"siyuan-note/siyuan","slug":"attribute-view-custom-colors-must-not-be-empty","errorCode":null,"errorMessage":"attribute view custom colors must not be empty","messagePattern":"attribute view custom colors must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view.go","lineNumber":8885,"sourceCode":"\t\t\tOrder  []string                       `json:\"order\"`\n\t\t}{}\n\t\tif err = gulu.JSON.UnmarshalJSON(data, &decoded); nil != err {\n\t\t\treturn\n\t\t}\n\t\treturn decoded.Colors, decoded.Order, nil\n\t}\n\tcolors = []*av.AttributeViewCustomColor{}\n\terr = gulu.JSON.UnmarshalJSON(data, &colors)\n\treturn\n}\n\nfunc setAttrViewCustomColors(operation *Operation) (err error) {\n\tattrView, err := avParseView(operation.AvID, operation.BlockID)\n\tif nil != err {\n\t\treturn\n\t}\n\tif nil == operation.Data {\n\t\treturn errors.New(\"attribute view custom colors must not be empty\")\n\t}\n\n\tdata, err := gulu.JSON.MarshalJSON(operation.Data)\n\tif nil != err {\n\t\treturn\n\t}\n\tcolors, order, err := parseAttrViewCustomColorsData(data)\n\tif nil != err {\n\t\treturn\n\t}\n\tcolors, err = av.NormalizeAttributeViewCustomColors(colors, true)\n\tif nil != err {\n\t\treturn\n\t}\n\n\toldColors := []*av.AttributeViewCustomColor{}\n\toldOrder := []string{}\n\tif styles, stylesErr := GetInlineStyles(); nil == stylesErr && styles != nil && styles.AV != nil {","sourceCodeStart":8867,"sourceCodeEnd":8903,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view.go#L8867-L8903","documentation":"setAttrViewCustomColors requires operation.Data to carry the new custom color palette; a nil Data payload is rejected because there is nothing to persist. The kernel marshals Data to JSON to build the color set, so the payload must be present and a JSON-marshalable structure.","triggerScenarios":"Calling the set-attribute-view-custom-colors API with no 'data' field in the request body, or explicitly setting data to null.","commonSituations":"Plugin sending an options object where only some fields are populated; frontend refactor dropping the data field; generic API wrapper that strips null/undefined fields incorrectly.","solutions":["Include a non-null data payload containing the colors (and any order fields) in the request","Check the API client isn't dropping undefined/null fields before sending","Pass an empty-but-valid structure if the intent is to represent no colors, per the API's expected schema","Refer to the kernel handler for the exact expected data shape"],"exampleFix":"// before\nfetchPost(\"/api/av/setAttrViewCustomColors\", {avID, blockID, data: null})\n// after\nfetchPost(\"/api/av/setAttrViewCustomColors\", {avID, blockID, data: {colors: [...], order: [...]}})","handlingStrategy":"validation","validationCode":"if (request.data == null) throw new Error(\"setAttrViewCustomColors requires a non-null data payload\");","typeGuard":null,"tryCatchPattern":"try { await setCustomColors(req); } catch (e) { if (String(e).includes(\"custom colors must not be empty\")) { /* populate req.data with the palette */ } else { throw e; } }","preventionTips":["Always construct the full data object (colors + order) before calling the API","Check API wrappers for null/undefined field stripping","Validate request bodies in plugin code before sending"],"tags":["attribute-view","validation","payload"],"backgroundTag":"empty-required-field","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"}