siyuan-note/siyuan · error

inline style [%s] must define color or backgroundColor

Error message

inline style [%s] must define color or backgroundColor

What it means

After both themes validate, the style must actually style something: at least one of Color or BackgroundColor must be non-empty in the light theme (the dark theme is checked symmetrically via the same-fields rule). A style with no colors in either field carries no visual effect and is rejected.

Source

Thrown at kernel/model/inline_style.go:693

		if maxInlineStyleNameRunes < utf8.RuneCountInString(name) {
			return nil, fmt.Errorf("inline style name exceeds the %d character limit", maxInlineStyleNameRunes)
		}
		if style.Light == nil || style.Dark == nil {
			return nil, fmt.Errorf("inline style [%s] must define light and dark themes", id)
		}

		light, err := normalizeInlineStyleTheme(style.Light)
		if err != nil {
			return nil, fmt.Errorf("invalid light theme of inline style [%s]: %w", id, err)
		}
		dark, err := normalizeInlineStyleTheme(style.Dark)
		if err != nil {
			return nil, fmt.Errorf("invalid dark theme of inline style [%s]: %w", id, err)
		}
		lightColor, lightBackground := light.Color != "", light.BackgroundColor != ""
		darkColor, darkBackground := dark.Color != "", dark.BackgroundColor != ""
		if !lightColor && !lightBackground {
			return nil, fmt.Errorf("inline style [%s] must define color or backgroundColor", id)
		}
		if lightColor != darkColor || lightBackground != darkBackground {
			return nil, fmt.Errorf("inline style [%s] must use the same fields in light and dark themes", id)
		}

		ret = append(ret, &InlineStyle{ID: id, Name: name, Hidden: style.Hidden, Light: light, Dark: dark})
	}
	return ret, nil
}

func normalizeInlineStyleBuiltin(builtin *InlineStyleBuiltin) (ret *InlineStyleBuiltin, err error) {
	ret = newEmptyInlineStyleBuiltin()
	if builtin == nil {
		return ret, nil
	}

	colorIndexes := make(map[int]struct{}, len(builtin.Colors))
	for _, color := range builtin.Colors {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Set at least Color or BackgroundColor (in both themes) before saving the style list.
  2. Drop placeholder entries that have no colors from the list.
  3. Fix the on-disk JSON by adding a color or removing the empty entry so loadInlineStyles succeeds.

Example fix

// before
&InlineStyle{ID: "20240101120000-abcdefg", Name: "Red",
  Light: &InlineStyleTheme{}, Dark: &InlineStyleTheme{}}
// after
&InlineStyle{ID: "20240101120000-abcdefg", Name: "Red",
  Light: &InlineStyleTheme{Color: "#ff0000"}, Dark: &InlineStyleTheme{Color: "#ff0000"}}
Defensive patterns

Strategy: validation

Validate before calling

for (const s of styles) { const t = s.Light; if (!t.Color && !t.BackgroundColor) throw new Error(`style ${s.ID} has no color or backgroundColor`); }

Type guard

const hasVisualEffect = (t: InlineStyleTheme): boolean => t.Color !== '' || t.BackgroundColor !== '';

Try / catch

try { await saveStyles(styles) } catch (e) { if (/must define color or backgroundColor/.test(String(e))) { /* remove the empty style or set a color, then retry */ } }

Prevention

When it happens

Trigger: setInlineStylesData receives an InlineStyle whose Light theme has both Color and BackgroundColor empty (dark likewise); loadInlineStyles reads a JSON entry with both color fields empty or absent in both themes.

Common situations: Creating a style entry reserved for later and saving before filling colors; a UI flow that clears both color pickers; migration tools copying style shells without theme payloads.

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/ec6f6466e6fecea9. Report an issue: GitHub.