siyuan-note/siyuan · error

attribute view custom color [%d] is still in use

Error message

attribute view custom color [%d] is still in use

What it means

When replacing the workspace attribute-view color palette, custom colors that are still referenced by remaining attribute view indexes cannot be removed. If an old custom color's index is not retained (not in the new palette) but is still used by some view, the kernel aborts with this error naming the color index.

Source

Thrown at kernel/model/attribute_view.go:8938

	if nil == order {
		order = oldOrder
	}

	retainedIndexes := map[int]struct{}{}
	for _, color := range colors {
		retainedIndexes[color.Index] = struct{}{}
	}
	usedIndexes := map[int]struct{}{}
	for _, index := range attrView.UsedCustomColorIndexes() {
		usedIndexes[index] = struct{}{}
	}
	for _, oldColor := range oldColors {
		if nil == oldColor {
			continue
		}
		if _, retained := retainedIndexes[oldColor.Index]; !retained {
			if _, used := usedIndexes[oldColor.Index]; used {
				return fmt.Errorf("attribute view custom color [%d] is still in use", oldColor.Index)
			}
		}
	}

	if err = replaceWorkspaceAVPalette(colors, order); nil != err {
		return
	}
	ReloadAttrView(attrView.ID)
	for _, relatedAvID := range av.GetSrcAvIDs(attrView.ID) {
		if relatedAvID != attrView.ID {
			ReloadAttrView(relatedAvID)
		}
	}
	return
}

func (tx *Transaction) doRemoveAttrViewColOption(operation *Operation) (ret *TxErr) {
	err := removeAttributeViewColumnOption(operation)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Keep the in-use custom color in the new palette (retain its index)
  2. Re-color the views/cells using that index first, then retry the palette update
  3. Query which attribute views use the color index and update them before pruning

Example fix

// before
replacePalette(newColors) // dropped color index 3 still used
// after
recolorViewsUsingIndex(3, replacementColor); replacePalette(newColors)
Defensive patterns

Strategy: fallback

Validate before calling

const inUse = getPaletteColorIndexesInUse(); if (newColors.some(c => inUse.has(c.index) === false && wasCustom(c.index))) await recolorViews();

Try / catch

try { await setPalette(colors, order); } catch (e) { const m = String(e).match(/color \[(\d+)\] is still in use/); if (m) { await recolorIndex(Number(m[1])); await setPalette(colors, order); } else throw e; }

Prevention

When it happens

Trigger: Calling the palette/custom-colors update API (replaceWorkspaceAVPalette path) with a new color list that drops a custom color whose index is still assigned to cells in some attribute view.

Common situations: Cleaning up the palette while views still reference old colors; syncing palettes between workspaces; automated palette pruning without checking usage.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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