siyuan-note/siyuan · error

card cover source [%s] does not match view [%s]

Error message

card cover source [%s] does not match view [%s]

What it means

Thrown by setAttrViewCardCoverPosition when the source in the operation (either 'content' or 'asset:<keyID>') does not equal the source currently configured on the target view (Gallery.CoverFrom/Gallery.CoverFromAssetKeyID or the Kanban equivalents). The view's cover configuration is the source of truth; positions are stored per source per item, so a mismatch is rejected to avoid orphaned positions.

Source

Thrown at kernel/model/attribute_view.go:1949

	}
	if nil == attrView.GetBlockValue(operation.RowID) {
		return fmt.Errorf("attribute view item [%s] not found", operation.RowID)
	}
	view, err := getAttrViewViewByBlockID(attrView, operation.BlockID)
	if nil != err {
		return
	}
	if av.LayoutTypeGallery != view.LayoutType && av.LayoutTypeKanban != view.LayoutType {
		return av.ErrWrongLayoutType
	}
	var source string
	if av.LayoutTypeGallery == view.LayoutType {
		source = av.CardCoverSource(view.Gallery.CoverFrom, view.Gallery.CoverFromAssetKeyID)
	} else {
		source = av.CardCoverSource(view.Kanban.CoverFrom, view.Kanban.CoverFromAssetKeyID)
	}
	if data.Source != source {
		return fmt.Errorf("card cover source [%s] does not match view [%s]", data.Source, view.ID)
	}
	if keyID := av.CardCoverSourceAssetKeyID(data.Source); "" != keyID {
		key, getErr := attrView.GetKey(keyID)
		if nil != getErr || nil == key || av.KeyTypeMAsset != key.Type {
			return fmt.Errorf("card cover asset field [%s] not found", keyID)
		}
	}

	attrView.SetCardCoverPosition(operation.RowID, data.Source, data.Position)
	err = av.SaveAttributeView(attrView)
	return
}

func AppendAttributeViewDetachedBlocksWithValues(avID string, blocksValues [][]*av.Value) (err error) {
	attrView, err := av.ParseAttributeView(avID)
	if err != nil {
		logging.LogErrorf("parse attribute view [%s] failed: %s", avID, err)
		return

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Re-read the view's cover configuration (CoverFrom / CoverFromAssetKeyID) on the client after any change and rebuild the source string with av.CardCoverSource before sending position updates.
  2. Drop queued cover-position operations when the cover source changes, and re-issue them against the new source.
  3. If the source was intentionally reset, send position: null to clear the old entry instead of an update with the stale source.

Example fix

// before
op.Data = { source: 'asset:20240101000000-old', position }
// after
const view = await getAttrViewView(avID, blockID)
const source = view.gallery.coverFrom === 'asset'
  ? `asset:${view.gallery.coverFromAssetKeyID}`
  : 'content'
op.Data = { source, position }
Defensive patterns

Strategy: validation

Validate before calling

const view = await getAttrViewView(avID, blockID)
const cfg = view.layout === 'gallery' ? view.gallery : view.kanban
const expectedSource = cfg.coverFrom === 'content' ? 'content'
  : cfg.coverFromAssetKeyID ? `asset:${cfg.coverFromAssetKeyID}` : ''
if (op.data.source !== expectedSource) { /* rebuild op or drop it */ }

Try / catch

try { await performTx([op]) } catch (e) {
  if (/card cover source .* does not match view/.test(String(e.message ?? e))) { reloadViewCoverConfig(avID, blockID) } else { throw e }
}

Prevention

When it happens

Trigger: User changed the gallery/kanban cover source (e.g. switched from content image to an asset field) and a pending/stale cover-position operation from the old source is still processed; the client sends the wrong source string; concurrent edits from two clients where one reconfigured the cover.

Common situations: Frontend did not re-read the view's CoverFrom/CoverFromAssetKeyID after the user changed the cover config; an undo/redo replay sends an operation with a source that no longer matches; multi-window sync race.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/6d8ab46396523ced. Report an issue: GitHub.