{"record":{"id":"6d8ab46396523ced","repo":"siyuan-note/siyuan","slug":"card-cover-source-s-does-not-match-view-s","errorCode":null,"errorMessage":"card cover source [%s] does not match view [%s]","messagePattern":"card cover source \\[(.+?)\\] does not match view \\[(.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view.go","lineNumber":1949,"sourceCode":"\t}\n\tif nil == attrView.GetBlockValue(operation.RowID) {\n\t\treturn fmt.Errorf(\"attribute view item [%s] not found\", operation.RowID)\n\t}\n\tview, err := getAttrViewViewByBlockID(attrView, operation.BlockID)\n\tif nil != err {\n\t\treturn\n\t}\n\tif av.LayoutTypeGallery != view.LayoutType && av.LayoutTypeKanban != view.LayoutType {\n\t\treturn av.ErrWrongLayoutType\n\t}\n\tvar source string\n\tif av.LayoutTypeGallery == view.LayoutType {\n\t\tsource = av.CardCoverSource(view.Gallery.CoverFrom, view.Gallery.CoverFromAssetKeyID)\n\t} else {\n\t\tsource = av.CardCoverSource(view.Kanban.CoverFrom, view.Kanban.CoverFromAssetKeyID)\n\t}\n\tif data.Source != source {\n\t\treturn fmt.Errorf(\"card cover source [%s] does not match view [%s]\", data.Source, view.ID)\n\t}\n\tif keyID := av.CardCoverSourceAssetKeyID(data.Source); \"\" != keyID {\n\t\tkey, getErr := attrView.GetKey(keyID)\n\t\tif nil != getErr || nil == key || av.KeyTypeMAsset != key.Type {\n\t\t\treturn fmt.Errorf(\"card cover asset field [%s] not found\", keyID)\n\t\t}\n\t}\n\n\tattrView.SetCardCoverPosition(operation.RowID, data.Source, data.Position)\n\terr = av.SaveAttributeView(attrView)\n\treturn\n}\n\nfunc AppendAttributeViewDetachedBlocksWithValues(avID string, blocksValues [][]*av.Value) (err error) {\n\tattrView, err := av.ParseAttributeView(avID)\n\tif err != nil {\n\t\tlogging.LogErrorf(\"parse attribute view [%s] failed: %s\", avID, err)\n\t\treturn","sourceCodeStart":1931,"sourceCodeEnd":1967,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/attribute_view.go#L1931-L1967","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Drop queued cover-position operations when the cover source changes, and re-issue them against the new source.","If the source was intentionally reset, send position: null to clear the old entry instead of an update with the stale source."],"exampleFix":"// before\nop.Data = { source: 'asset:20240101000000-old', position }\n// after\nconst view = await getAttrViewView(avID, blockID)\nconst source = view.gallery.coverFrom === 'asset'\n  ? `asset:${view.gallery.coverFromAssetKeyID}`\n  : 'content'\nop.Data = { source, position }","handlingStrategy":"validation","validationCode":"const view = await getAttrViewView(avID, blockID)\nconst cfg = view.layout === 'gallery' ? view.gallery : view.kanban\nconst expectedSource = cfg.coverFrom === 'content' ? 'content'\n  : cfg.coverFromAssetKeyID ? `asset:${cfg.coverFromAssetKeyID}` : ''\nif (op.data.source !== expectedSource) { /* rebuild op or drop it */ }","typeGuard":null,"tryCatchPattern":"try { await performTx([op]) } catch (e) {\n  if (/card cover source .* does not match view/.test(String(e.message ?? e))) { reloadViewCoverConfig(avID, blockID) } else { throw e }\n}","preventionTips":["Re-read CoverFrom/CoverFromAssetKeyID after the user changes the cover source.","Drop queued position operations when the source changes.","Send position:null with the old source to clear stale entries."],"tags":["attribute-view","card-cover","stale-state","transaction"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}