{"record":{"id":"5f759c43988b8d19","repo":"siyuan-note/siyuan","slug":"inline-style-s-must-define-light-and-dark-theme","errorCode":null,"errorMessage":"inline style [%s] must define light and dark themes","messagePattern":"inline style \\[(.+?)\\] must define light and dark themes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/inline_style.go","lineNumber":679,"sourceCode":"\t\t\t}\n\t\t}\n\t\tif !ast.IsNodeIDPattern(id) {\n\t\t\treturn nil, fmt.Errorf(\"invalid inline style ID [%s]\", id)\n\t\t}\n\t\tif _, exists := ids[id]; exists {\n\t\t\treturn nil, fmt.Errorf(\"duplicate inline style ID [%s]\", id)\n\t\t}\n\t\tids[id] = struct{}{}\n\n\t\tname := strings.TrimSpace(style.Name)\n\t\tif name == \"\" {\n\t\t\treturn nil, errors.New(\"inline style name must not be empty\")\n\t\t}\n\t\tif maxInlineStyleNameRunes < utf8.RuneCountInString(name) {\n\t\t\treturn nil, fmt.Errorf(\"inline style name exceeds the %d character limit\", maxInlineStyleNameRunes)\n\t\t}\n\t\tif style.Light == nil || style.Dark == nil {\n\t\t\treturn nil, fmt.Errorf(\"inline style [%s] must define light and dark themes\", id)\n\t\t}\n\n\t\tlight, err := normalizeInlineStyleTheme(style.Light)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid light theme of inline style [%s]: %w\", id, err)\n\t\t}\n\t\tdark, err := normalizeInlineStyleTheme(style.Dark)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid dark theme of inline style [%s]: %w\", id, err)\n\t\t}\n\t\tlightColor, lightBackground := light.Color != \"\", light.BackgroundColor != \"\"\n\t\tdarkColor, darkBackground := dark.Color != \"\", dark.BackgroundColor != \"\"\n\t\tif !lightColor && !lightBackground {\n\t\t\treturn nil, fmt.Errorf(\"inline style [%s] must define color or backgroundColor\", id)\n\t\t}\n\t\tif lightColor != darkColor || lightBackground != darkBackground {\n\t\t\treturn nil, fmt.Errorf(\"inline style [%s] must use the same fields in light and dark themes\", id)\n\t\t}","sourceCodeStart":661,"sourceCodeEnd":697,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/inline_style.go#L661-L697","documentation":"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.","triggerScenarios":"setInlineStylesData receives an InlineStyle with Light or Dark nil; loadInlineStyles reads a JSON entry where the light or dark key is absent/null.","commonSituations":"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.","solutions":["Always set both Light and Dark theme objects, copying the same colors if the style should look identical in both modes.","Fix the JSON file on disk by adding the missing light/dark object.","Wrap the API call and recover by re-submitting the list with the missing theme defaulted from the present one."],"exampleFix":"// before\nstyle := &InlineStyle{ID: \"20240101120000-abcdefg\", Name: \"Red\", Light: &InlineStyleTheme{Color: \"#ff0000\"}} // Dark missing\n// after\ntheme := &InlineStyleTheme{Color: \"#ff0000\"}\nstyle := &InlineStyle{ID: \"20240101120000-abcdefg\", Name: \"Red\", Light: theme, Dark: &InlineStyleTheme{Color: \"#ff0000\"}}","handlingStrategy":"type-guard","validationCode":"for (const s of styles) { if (!s.Light || !s.Dark) throw new Error(`style ${s.ID} needs both light and dark themes`); }","typeGuard":"const hasBothThemes = (s: InlineStyle): s is InlineStyle & {Light: InlineStyleTheme, Dark: InlineStyleTheme} => !!s.Light && !!s.Dark;","tryCatchPattern":"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 */ } }","preventionTips":["Default the dark theme from the light theme (or vice versa) at construction time","Always construct styles through a factory that fills both themes","Never omit theme keys when writing the JSON by hand"],"tags":["validation","missing-field","inline-styles","theming"],"backgroundTag":"missing-required-config-field","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}