siyuan-note/siyuan · error
%s must use the same fields in light and dark themes
Error message
%s must use the same fields in light and dark themes
What it means
SiYuan inline-style color themes are stored as a paired light/dark theme, and both halves must fill in exactly the same set of fields. After normalization the code computes booleans for whether Color and BackgroundColor are present in each half; if the presence pattern differs between light and dark (e.g. light sets only Color, dark sets only BackgroundColor), the theme is rejected. This keeps rendering consistent when the user switches appearance mode.
Source
Thrown at kernel/model/inline_style.go:807
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)
}
if _, exists := seen[index]; exists {
continueView on GitHub (pinned to 8641553a1f)
Solutions
- Ensure the dark theme defines exactly the same fields (color and/or backgroundColor) as the light theme.
- If a field should be empty in dark mode, explicitly set it to "" in both halves rather than omitting it in only one.
- Generate both theme halves from the same struct/template so their field sets cannot diverge.
Example fix
// before
theme := &InlineStyleTheme{
Light: &InlineStyleThemeHalf{Color: "#ff0000"},
Dark: &InlineStyleThemeHalf{BackgroundColor: "#003300"}, // mismatched fields
}
// after
theme := &InlineStyleTheme{
Light: &InlineStyleThemeHalf{Color: "#ff0000", BackgroundColor: ""},
Dark: &InlineStyleThemeHalf{Color: "#00ff00", BackgroundColor: ""}, // same fields
} Defensive patterns
Strategy: validation
Validate before calling
function themeFieldsMatch(light, dark) {
const has = (t, k) => Boolean(t && typeof t[k] === "string" && t[k] !== "");
return has(light, "color") === has(dark, "color") &&
has(light, "backgroundColor") === has(dark, "backgroundColor");
} Prevention
- Always construct light and dark halves from the same template with both fields present (use "" for absent).
- Write a small client-side parity check before saving any theme pair.
- Never build the dark half from a different code path than the light half.
When it happens
Trigger: Calling any API that saves a custom inline style theme (normalizeInlineStyleThemes path in kernel/model/inline_style.go) with a light theme defining {color} and a dark theme defining {backgroundColor}, or one half empty while the other defines a field, or one half defining both fields and the other only one.
Common situations: Developers hand-crafting theme JSON for plugin/UI customization, migrating old single-theme configs to the paired format, or programmatically generating themes where the dark variant was built from a different template than the light variant.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- theme [%s] not exists or not available for light mode
- theme [%s] not exists or not available for dark mode
- hidden builtin %s index [%d] must be between %d and %d
- invalid hidden builtin style ID [%s]
- invalid agent permission mode
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/e89e2d72018ed01f.
Report an issue: GitHub.