siyuan-note/siyuan · error

workspace attribute view palette update must not be null

Error message

workspace attribute view palette update must not be null

What it means

SetWorkspaceAVPalette is the kernel API for replacing the workspace attribute-view color palette stored in inline styles. It requires a non-nil *WorkspaceAVPaletteUpdate; a nil update would clear the palette unintentionally, so it returns this error immediately (after waiting for syncing storages and acquiring the lock).

Source

Thrown at kernel/model/inline_style.go:180

	waitForSyncingStorages()
	inlineStylesLock.Lock()
	defer inlineStylesLock.Unlock()

	current, err := loadInlineStyles()
	if err != nil {
		return nil, false, err
	}
	return setInlineStylesData(styles, current.AV)
}

// 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)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure the API request body contains a valid WorkspaceAVPaletteUpdate object before calling the kernel
  2. In the frontend handler, reject/validate empty payloads with a 400 before invoking SetWorkspaceAVPalette
  3. In Go callers/tests, construct the update struct (e.g. &WorkspaceAVPaletteUpdate{Colors: ..., Order: ...}) instead of passing nil

Example fix

// before: nil update passed straight through
var update *model.WorkspaceAVPaletteUpdate
model.SetWorkspaceAVPalette(update) // error
// after
update := &model.WorkspaceAVPaletteUpdate{Colors: colors, Order: order}
if update == nil {
	return nil, errors.New("palette update required")
}
model.SetWorkspaceAVPalette(update)
Defensive patterns

Strategy: type-guard

Validate before calling

if update == nil {
	return errors.New("palette update required")
}

Type guard

func hasPaletteUpdate(u *model.WorkspaceAVPaletteUpdate) bool { return u != nil }

Try / catch

ret, changed, err := model.SetWorkspaceAVPalette(update)
if err != nil && strings.Contains(err.Error(), "must not be null") {
	// request body was empty; return 400 to the client
}

Prevention

When it happens

Trigger: Calling model.SetWorkspaceAVPalette(nil), e.g. from the api handler setWorkspaceAVPalette when the request payload failed to decode into a non-nil update object, or from a test passing nil.

Common situations: Frontend sends an empty/missing JSON body so Gin binds a nil pointer; plugin constructs the update struct incorrectly and passes nil; refactoring renamed the field so decoding yields nil.

Related errors


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