{"record":{"id":"7b0fcf59e906bbea","repo":"siyuan-note/siyuan","slug":"duplicated-attribute-view-custom-color-index-d","errorCode":null,"errorMessage":"duplicated attribute view custom color index [%d]","messagePattern":"duplicated attribute view custom color index \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/av/color.go","lineNumber":110,"sourceCode":"\n// NormalizeAttributeViewCustomColors 校验并规范化数据库自定义颜色。\nfunc NormalizeAttributeViewCustomColors(colors []*AttributeViewCustomColor, strict bool) (ret []*AttributeViewCustomColor, err error) {\n\tif strict && MaxCustomColors < len(colors) {\n\t\treturn nil, fmt.Errorf(\"attribute view custom colors count exceeds the %d item limit\", MaxCustomColors)\n\t}\n\n\tindexes := map[int]struct{}{}\n\tfor _, color := range colors {\n\t\tnormalized, normalizeErr := normalizeAttributeViewCustomColor(color)\n\t\tif nil != normalizeErr {\n\t\t\tif strict {\n\t\t\t\treturn nil, normalizeErr\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := indexes[normalized.Index]; ok {\n\t\t\tif strict {\n\t\t\t\treturn nil, fmt.Errorf(\"duplicated attribute view custom color index [%d]\", normalized.Index)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tindexes[normalized.Index] = struct{}{}\n\t\tret = append(ret, normalized)\n\t}\n\n\tsort.Slice(ret, func(i, j int) bool {\n\t\treturn ret[i].Index < ret[j].Index\n\t})\n\tif nil == ret {\n\t\tret = []*AttributeViewCustomColor{}\n\t}\n\treturn\n}\n\nfunc normalizeAttributeViewCustomColor(color *AttributeViewCustomColor) (ret *AttributeViewCustomColor, err error) {\n\tif nil == color {","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/av/color.go#L92-L128","documentation":"NormalizeAttributeViewCustomColors returns this error in strict mode when two custom colors share the same Index. Each custom color index is a unique slot; duplicates are ambiguous for rendering, so strict mode rejects them while non-strict mode silently drops later duplicates.","triggerScenarios":"Calling av.NormalizeAttributeViewCustomColors(colors, true) where two entries carry the same Index (color.go:108); decoding a palette (decodeHistoricalWorkspacePalette) or applying colors via setAttrViewCustomColors with duplicated indexes.","commonSituations":"Merging two attribute views' color definitions without reindexing; a script appending new colors reusing an existing index instead of av.NextCustomColorIndex; hand-edited .av JSON with copy-pasted color blocks.","solutions":["Reassign duplicate entries with av.NextCustomColorIndex (or the next free index from UsedCustomColorIndexes) before normalizing.","Dedupe by index first: keep one entry per index (merge Hidden/Light/Dark) and drop or merge the rest.","Call NormalizeAttributeViewCustomColors with strict=false to silently drop duplicates if lossy handling is acceptable.","When generating colors programmatically, always allocate via NextCustomColorIndex instead of hardcoded indexes."],"exampleFix":"// before\ncolors := []*av.AttributeViewCustomColor{{Index: 15, ...}, {Index: 15, ...}} // duplicate\nret, err := av.NormalizeAttributeViewCustomColors(colors, true)\n\n// after\ncolors[1].Index = av.NextCustomColorIndex(attrView)\nret, err := av.NormalizeAttributeViewCustomColors(colors, true)","handlingStrategy":"validation","validationCode":"func hasDuplicateIndexes(colors []*av.AttributeViewCustomColor) bool {\n\tseen := map[int]struct{}{}\n\tfor _, c := range colors {\n\t\tif _, ok := seen[c.Index]; ok { return true }\n\t\tseen[c.Index] = struct{}{}\n\t}\n\treturn false\n}","typeGuard":null,"tryCatchPattern":"if _, err := av.NormalizeAttributeViewCustomColors(colors, true); err != nil && strings.Contains(err.Error(), \"duplicated\") {\n\t// reassign with av.NextCustomColorIndex and retry\n}","preventionTips":["Always allocate color indexes via av.NextCustomColorIndex","Reindex entries when merging two views' palettes","Use strict=false if silently dropping duplicates is acceptable"],"tags":["attribute-view","custom-color","duplicate","validation","siyuan-kernel"],"backgroundTag":"conflicting-config-options","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}