siyuan-note/siyuan · error

%s must define color or backgroundColor

Error message

%s must define color or backgroundColor

What it means

A builtin color or builtin style defines neither `color` nor `backgroundColor` in its light theme (and by symmetry the entry is visually empty). Each builtin entry must set at least one of the two fields in both light and dark themes; an entry that sets nothing has no rendering effect, so normalizeInlineStyleThemePair rejects it. The description names the offending entry.

Source

Thrown at kernel/model/inline_style.go:804

	ret.Order = av.NormalizeAttributeViewColorOrder(palette.Order, colors)
	return ret, nil
}

func normalizeInlineStyleThemePair(light, dark *InlineStyleTheme, description string) (normalizedLight,
	normalizedDark *InlineStyleTheme, err error) {
	if light == nil || dark == nil {
		return nil, nil, fmt.Errorf("%s must define light and dark themes", description)
	}
	if normalizedLight, err = normalizeInlineStyleTheme(light); err != nil {
		return nil, nil, fmt.Errorf("invalid light theme of %s: %w", description, err)
	}
	if normalizedDark, err = normalizeInlineStyleTheme(dark); err != nil {
		return nil, nil, fmt.Errorf("invalid dark theme of %s: %w", description, err)
	}
	lightColor, lightBackground := normalizedLight.Color != "", normalizedLight.BackgroundColor != ""
	darkColor, darkBackground := normalizedDark.Color != "", normalizedDark.BackgroundColor != ""
	if !lightColor && !lightBackground {
		return nil, nil, fmt.Errorf("%s must define color or backgroundColor", description)
	}
	if lightColor != darkColor || lightBackground != darkBackground {
		return nil, nil, fmt.Errorf("%s must use the same fields in light and dark themes", description)
	}
	return normalizedLight, normalizedDark, nil
}

func normalizeHiddenBuiltinColorIndexes(indexes []int, field string) (ret []int, err error) {
	ret = make([]int, 0, len(indexes))
	seen := make(map[int]struct{}, len(indexes))
	maxIndex := maxBuiltinColorIndex
	if field == "av" {
		maxIndex = neutralAVColorIndex
	}
	for _, index := range indexes {
		if index < minBuiltinColorIndex || maxIndex < index {
			return nil, fmt.Errorf("hidden builtin %s index [%d] must be between %d and %d", field, index,
				minBuiltinColorIndex, maxIndex)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Set a color or backgroundColor (as #rrggbb hex) in both the light and dark themes of the entry named in the message.
  2. If the entry is not needed, remove it from the builtin array entirely.
  3. Add a save-time check that each entry has at least one non-empty theme field in both modes.

Example fix

// before
{"index": 7, "light": {}, "dark": {}}
// after
{"index": 7, "light": {"backgroundColor": "#fbe9e7"}, "dark": {"backgroundColor": "#4a2b27"}}
Defensive patterns

Strategy: validation

Validate before calling

for (const entry of entries) {
  for (const mode of ["light", "dark"]) {
    const t = entry[mode];
    if (!(t.color || t.backgroundColor)) {
      throw new Error(`${describe(entry)} ${mode} theme must define color or backgroundColor`);
    }
  }
}

Type guard

function hasVisibleTheme(t) {
  return typeof t.color === "string" && t.color !== "" || typeof t.backgroundColor === "string" && t.backgroundColor !== "";
}

Prevention

When it happens

Trigger: setInlineStylesData or loadInlineStyles with a builtin entry whose light and dark theme objects are both empty or omit both color and backgroundColor, e.g. `{"index": 7, "light": {}, "dark": {}}`.

Common situations: Creating a placeholder entry intending to fill values later; a UI/form that cleared both fields but still saved; stripping fields with a sanitizer that removed "empty" values.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/803fcfdba2bdeff0. Report an issue: GitHub.