siyuan-note/siyuan · error

attribute view custom color must not be null

Error message

attribute view custom color must not be null

What it means

normalizeAttributeViewCustomColor rejects a nil AttributeViewCustomColor pointer. A null entry in the color list cannot be normalized or persisted, so it is treated as an explicit error rather than skipped.

Solutions

  1. Filter out nil entries from the slice before calling NormalizeAttributeViewCustomColors
  2. Fix the source JSON to remove null items
  3. Initialize all slice elements when constructing colors programmatically

Example fix

// before
ret, err := av.NormalizeAttributeViewCustomColors(colors, true)
// after
var filtered []*av.AttributeViewCustomColor
for _, c := range colors {
    if c != nil { filtered = append(filtered, c) }
}
ret, err := av.NormalizeAttributeViewCustomColors(filtered, true)
Defensive patterns

Strategy: type-guard

Validate before calling

for i, c := range colors {
    if c == nil { return fmt.Errorf("nil color at %d", i) }
}

Type guard

func validColors(colors []*av.AttributeViewCustomColor) []*av.AttributeViewCustomColor {
    var out []*av.AttributeViewCustomColor
    for _, c := range colors { if c != nil { out = append(out, c) } }
    return out
}

Try / catch

if _, err := av.NormalizeAttributeViewCustomColors(colors, true); err != nil {
    if strings.Contains(err.Error(), "must not be null") { colors = validColors(colors) }
}

Prevention

When it happens

Trigger: Passing a slice containing nil elements to NormalizeAttributeViewCustomColors, e.g. from JSON decoding of a palette that contains literal null entries, or building the slice with a fixed-length array leaving trailing nils.

Common situations: Hand-edited palette JSON containing `null` array items; make([]*AttributeViewCustomColor, n) with unfilled slots; config merging that inserts nil placeholders.

Related errors


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

Appendix: source

Thrown at kernel/av/color.go:129

			}
			continue
		}
		indexes[normalized.Index] = struct{}{}
		ret = append(ret, normalized)
	}

	sort.Slice(ret, func(i, j int) bool {
		return ret[i].Index < ret[j].Index
	})
	if nil == ret {
		ret = []*AttributeViewCustomColor{}
	}
	return
}

func normalizeAttributeViewCustomColor(color *AttributeViewCustomColor) (ret *AttributeViewCustomColor, err error) {
	if nil == color {
		return nil, errors.New("attribute view custom color must not be null")
	}
	if color.Index < CustomColorMinIndex || CustomColorMaxIndex < color.Index {
		return nil, fmt.Errorf("attribute view custom color index [%d] is out of range [%d, %d]",
			color.Index, CustomColorMinIndex, CustomColorMaxIndex)
	}

	ret = &AttributeViewCustomColor{Index: color.Index, Hidden: color.Hidden}
	ret.Light, err = normalizeAttributeViewColorTheme(color.Light)
	if nil != err {
		return nil, fmt.Errorf("invalid light theme of attribute view custom color [%d]: %w", color.Index, err)
	}
	ret.Dark, err = normalizeAttributeViewColorTheme(color.Dark)
	if nil != err {
		return nil, fmt.Errorf("invalid dark theme of attribute view custom color [%d]: %w", color.Index, err)
	}
	return
}

View on GitHub (pinned to 9f775e8a12)