{"record":{"id":"adfadcc2cac5e583","repo":"siyuan-note/siyuan","slug":"attribute-view-s-has-no-block-key","errorCode":null,"errorMessage":"attribute view [%s] has no block key","messagePattern":"attribute view \\[(.+?)\\] has no block key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view.go","lineNumber":2046,"sourceCode":"\t}\n\n\tReloadAttrView(avID)\n\treturn\n}\n\n// DuplicateAttributeViewRow 创建源行的纯文本副本，复制除主键、Rollup、Created、Updated 外的所有字段值，\n// 双向关联同步更新目标属性视图的反向关联列。新行 ID 由调用方（前端）生成并通过 newRowID 传入，\n// 副本插入到 previousItemID（通常为最后选中的条目）之后。\nfunc DuplicateAttributeViewRow(tx *Transaction, avID, previousItemID, srcRowID, newRowID string) (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\n\t}\n\n\tblockKeyValues := attrView.GetBlockKeyValues()\n\tif nil == blockKeyValues {\n\t\terr = fmt.Errorf(\"attribute view [%s] has no block key\", avID)\n\t\treturn\n\t}\n\n\tsrcBlockVal := blockKeyValues.GetValue(srcRowID)\n\tif nil == srcBlockVal {\n\t\terr = fmt.Errorf(\"source row [%s] not found in attribute view [%s]\", srcRowID, avID)\n\t\treturn\n\t}\n\n\tif \"\" == newRowID {\n\t\tnewRowID = ast.NewNodeID()\n\t}\n\n\tnow := util.CurrentTimeMillis()\n\n\t// 创建副本的主键值，强制为纯文本（非绑定块）\n\tblockContent := \"\"\n\tif nil != srcBlockVal.Block {","sourceCodeStart":2028,"sourceCodeEnd":2064,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/attribute_view.go#L2028-L2064","documentation":"Thrown by DuplicateAttributeViewRow when attrView.GetBlockKeyValues() returns nil, meaning the AttributeView has no primary key (KeyTypeBlock). Every AV must have exactly one block key serving as the primary column; without it, rows cannot be duplicated because the primary value cannot be cloned.","triggerScenarios":"The AV was created/imported without a block key, or the block key was somehow removed (corruption). DuplicateAttributeViewRow relies on the block key to clone the primary cell content into a new row.","commonSituations":"Hand-crafted or third-party .sy files that skip the primary key; a partial restore that dropped the block key; an AV that was migrated from an older format and never normalized.","solutions":["Repair the AV by ensuring it has a block-type primary key before allowing row duplication (re-create the key via the kernel's AV repair/normalization path).","Do not expose 'duplicate row' UI affordances if the AV lacks a primary key; guard at the call site.","Restore the AV from a known-good snapshot if the missing primary key indicates wider corruption."],"exampleFix":"// before\nDuplicateAttributeViewRow(tx, avID, prevID, srcRowID, newRowID) // AV has no block key\n// after\nif blockKV := attrView.GetBlockKeyValues(); blockKV == nil {\n  return errors.New(\"cannot duplicate: attribute view is missing its primary block key; repair the AV first\")\n}\nDuplicateAttributeViewRow(tx, avID, prevID, srcRowID, newRowID)","handlingStrategy":"validation","validationCode":"const av = await parseAttributeView(avID)\nconst hasBlockKey = av.keyValues.some(kv => kv.key.type === 'block')\nif (!hasBlockKey) { /* offer to repair/restore AV; do not call DuplicateAttributeViewRow */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hand-edit AV JSON to remove the block key.","Run AV repair/normalization before allowing row operations if the source is a third-party import."],"tags":["attribute-view","primary-key","corruption","row-duplicate"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}