siyuan-note/siyuan · error

duplicate workspace attribute view builtin color update [%d]

Error message

duplicate workspace attribute view builtin color update [%d]

What it means

SetWorkspaceAVPalette tracks already-seen builtin color indexes in updatedIndexes while iterating update.BuiltinColors. If two patches target the same index the update is ambiguous, so it fails with this error instead of applying the patches in undocumented order.

Source

Thrown at kernel/model/inline_style.go:198

		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{
				Index: patch.Index,
				Light: patch.Light,
				Dark:  patch.Dark,
			})
		}
		filteredHidden := current.Builtin.Hidden.AV[:0]
		for _, index := range current.Builtin.Hidden.AV {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Deduplicate BuiltinColors by Index (last-wins or first-wins per your intent) before calling SetWorkspaceAVPalette
  2. Build the patch list as a map[int]*InlineStyleBuiltinColorPatch during collection, then convert to a slice
  3. Fix the caller's merge/retry logic so the same index is never appended twice

Example fix

// before: duplicate indexes appended
patches = append(patches, &model.InlineStyleBuiltinColorPatch{Index: 3, Color: red})
patches = append(patches, &model.InlineStyleBuiltinColorPatch{Index: 3, Color: blue}) // duplicate
// after: dedupe by index, last wins
byIndex := map[int]*model.InlineStyleBuiltinColorPatch{}
byIndex[3] = &model.InlineStyleBuiltinColorPatch{Index: 3, Color: red}
byIndex[3] = &model.InlineStyleBuiltinColorPatch{Index: 3, Color: blue}
for _, p := range byIndex { patches = append(patches, p) }
Defensive patterns

Strategy: validation

Validate before calling

seen := map[int]bool{}
for _, p := range patches {
	if seen[p.Index] { return fmt.Errorf("duplicate patch index %d", p.Index) }
	seen[p.Index] = true
}

Try / catch

_, _, err := model.SetWorkspaceAVPalette(update)
if err != nil && strings.Contains(err.Error(), "duplicate workspace attribute view builtin color") {
	// deduplicate by index and retry
}

Prevention

When it happens

Trigger: Passing a WorkspaceAVPaletteUpdate whose BuiltinColors slice contains two or more patches with the same Index value — the second occurrence triggers the duplicate check in the loop.

Common situations: Frontend accumulates color edits without deduplicating by index before saving; a plugin merges multiple patch sets naively with append; retry logic appends a patch twice.

Related errors


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