siyuan-note/siyuan · error

workspace attribute view builtin color update must not be nu

Error message

workspace attribute view builtin color update must not be null

What it means

Within SetWorkspaceAVPalette, update.BuiltinColors may contain per-index patch entries. A nil entry in that slice cannot be applied, so the function aborts the whole palette update with this error rather than silently skipping part of the patch list.

Source

Thrown at kernel/model/inline_style.go:191

// SetWorkspaceAVPalette 只更新数据库颜色配置,保留其他窗口可能同时修改的行级样式设置。
func SetWorkspaceAVPalette(update *WorkspaceAVPaletteUpdate) (ret *InlineStyles, changed bool, err error) {
	waitForSyncingStorages()
	inlineStylesLock.Lock()
	defer inlineStylesLock.Unlock()

	if update == nil {
		return nil, false, errors.New("workspace attribute view palette update must not be null")
	}
	current, err := loadInlineStyles()
	if err != nil {
		return nil, false, err
	}
	currentAV := current.AV
	current.AV = &InlineStyleAV{Colors: update.Colors, Order: update.Order}
	updatedIndexes := map[int]struct{}{}
	for _, patch := range update.BuiltinColors {
		if patch == nil {
			return nil, false, errors.New("workspace attribute view builtin color update must not be null")
		}
		if patch.Index < minBuiltinColorIndex || neutralAVColorIndex < patch.Index {
			return nil, false, fmt.Errorf("builtin color index [%d] must be between %d and %d", patch.Index,
				minBuiltinColorIndex, neutralAVColorIndex)
		}
		if _, duplicated := updatedIndexes[patch.Index]; duplicated {
			return nil, false, fmt.Errorf("duplicate workspace attribute view builtin color update [%d]", patch.Index)
		}
		updatedIndexes[patch.Index] = struct{}{}
		filtered := current.Builtin.Colors[:0]
		for _, color := range current.Builtin.Colors {
			if color.Index != patch.Index {
				filtered = append(filtered, color)
			}
		}
		current.Builtin.Colors = filtered
		if patch.Customized {
			current.Builtin.Colors = append(current.Builtin.Colors, &InlineStyleBuiltinColor{

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Filter out nil entries from BuiltinColors before calling SetWorkspaceAVPalette
  2. Fix the caller (frontend/plugin) to never emit null elements in the builtinColors JSON array
  3. In Go, use a typed non-nil slice when constructing the update

Example fix

// before: patch list contains a nil entry
update := &model.WorkspaceAVPaletteUpdate{
	BuiltinColors: []*model.InlineStyleBuiltinColorPatch{patch1, nil},
}
model.SetWorkspaceAVPalette(update) // error
// after: filter nils first
patches := patches[:0]
for _, p := range patches {
	if p != nil { patches = append(patches, p) }
}
update.BuiltinColors = patches
Defensive patterns

Strategy: type-guard

Validate before calling

for i, p := range patches {
	if p == nil { return fmt.Errorf("builtin color patch %d is nil", i) }
}

Type guard

func validPatches(ps []*model.InlineStyleBuiltinColorPatch) bool {
	for _, p := range ps { if p == nil { return false } }
	return true
}

Try / catch

_, _, err := model.SetWorkspaceAVPalette(update)
if err != nil && strings.Contains(err.Error(), "builtin color update must not be null") {
	// filter nils and retry
}

Prevention

When it happens

Trigger: Passing a WorkspaceAVPaletteUpdate whose BuiltinColors slice contains a nil element, e.g. &WorkspaceAVPaletteUpdate{BuiltinColors: []*InlineStyleBuiltinColorPatch{nil}} from an API caller or test.

Common situations: Frontend serializes an array with holes/sparse entries that decode to nil pointers; a plugin builds the patch list dynamically and appends nil on a failed lookup; hand-written JSON like "builtinColors": [null].

Related errors


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