siyuan-note/siyuan · error

inline style [%s] must define light and dark themes

Error message

inline style [%s] must define light and dark themes

What it means

Each inline style must supply both a Light and a Dark theme object. Because the editor renders every style in both color schemes, a missing theme would leave one mode unstyled; normalizeInlineStyles rejects the entry when style.Light or style.Dark is nil.

Source

Thrown at kernel/model/inline_style.go:679

			}
		}
		if !ast.IsNodeIDPattern(id) {
			return nil, fmt.Errorf("invalid inline style ID [%s]", id)
		}
		if _, exists := ids[id]; exists {
			return nil, fmt.Errorf("duplicate inline style ID [%s]", id)
		}
		ids[id] = struct{}{}

		name := strings.TrimSpace(style.Name)
		if name == "" {
			return nil, errors.New("inline style name must not be empty")
		}
		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)
		}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Always set both Light and Dark theme objects, copying the same colors if the style should look identical in both modes.
  2. Fix the JSON file on disk by adding the missing light/dark object.
  3. Wrap the API call and recover by re-submitting the list with the missing theme defaulted from the present one.

Example fix

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

Strategy: type-guard

Validate before calling

for (const s of styles) { if (!s.Light || !s.Dark) throw new Error(`style ${s.ID} needs both light and dark themes`); }

Type guard

const hasBothThemes = (s: InlineStyle): s is InlineStyle & {Light: InlineStyleTheme, Dark: InlineStyleTheme} => !!s.Light && !!s.Dark;

Try / catch

try { await saveStyles(styles) } catch (e) { if (/must define light and dark themes/.test(String(e))) { styles = styles.map(s => ({...s, Dark: s.Dark ?? s.Light, Light: s.Light ?? s.Dark})); /* retry */ } }

Prevention

When it happens

Trigger: setInlineStylesData receives an InlineStyle with Light or Dark nil; loadInlineStyles reads a JSON entry where the light or dark key is absent/null.

Common situations: Building styles in code and populating only the currently visible theme; hand-written JSON omitting the dark theme; third-party tools exporting single-theme styles into SiYuan config.

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