{"record":{"id":"148e462899878a4e","repo":"siyuan-note/siyuan","slug":"related-database-entry-s-changed","errorCode":null,"errorMessage":"related database entry [%s] changed","messagePattern":"related database entry \\[(.+?)\\] changed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_items.go","lineNumber":356,"sourceCode":"\t\t\tif dest == nil {\n\t\t\t\tdest, err = av.ParseAttributeViewForIndexInBox(kv.Key.Relation.AvID, state.BoxID)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif dest == nil {\n\t\t\t\t\treturn av.ErrViewNotFound\n\t\t\t\t}\n\t\t\t\tchanged[dest.ID] = dest\n\t\t\t}\n\t\t\tback, backErr := dest.GetKeyValues(kv.Key.Relation.BackKeyID)\n\t\t\tif backErr != nil || back.Key.Type != av.KeyTypeRelation || back.Key.Relation == nil ||\n\t\t\t\t!back.Key.Relation.IsTwoWay || back.Key.Relation.AvID != state.AvID || back.Key.Relation.BackKeyID != kv.Key.ID {\n\t\t\t\treturn fmt.Errorf(\"database back relation field [%s] changed\", kv.Key.Relation.BackKeyID)\n\t\t\t}\n\t\t\tfor _, id := range value.Relation.BlockIDs {\n\t\t\t\tbackValue := back.GetValue(id)\n\t\t\t\tif dest.GetBlockValue(id) == nil || backValue == nil || backValue.Relation == nil {\n\t\t\t\t\treturn fmt.Errorf(\"related database entry [%s] changed\", id)\n\t\t\t\t}\n\t\t\t\tif !slices.Contains(backValue.Relation.BlockIDs, value.BlockID) {\n\t\t\t\t\tbackValue.Relation.BlockIDs = append(backValue.Relation.BlockIDs, value.BlockID)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfor _, view := range current.Views {\n\t\tview.ItemIDs = restoreAttributeViewItemOrder(view.ItemIDs, state.Orders[view.ID], state.ItemIDs)\n\t\tfor _, group := range view.Groups {\n\t\t\tgroup.GroupItemIDs = restoreAttributeViewItemOrder(group.GroupItemIDs, state.Orders[group.ID], state.ItemIDs)\n\t\t}\n\t}\n\tfor id, covers := range state.Covers {\n\t\tif covers != nil {\n\t\t\tif current.CardCoverPositions == nil {\n\t\t\t\tcurrent.CardCoverPositions = map[string]map[string]*av.CardCoverPosition{}\n\t\t\t}","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_items.go#L338-L374","documentation":"When replaying an insert containing two-way relation values, each referenced target row must still exist in the destination database and carry a relation value whose back-link list can be extended. If the target row, its back-relation value, or that value's relation data is missing, the kernel cannot merge the back-link without losing the target's other relations, so it fails with this error.","triggerScenarios":"Replaying doInsertAttrViewBlock whose relation values point to rows that were deleted from the target database, whose relation values were cleared, or whose relation value lost its Relation payload since the snapshot.","commonSituations":"Target rows removed after the original insert (detected on undo/redo); a plugin cleared relation values; sync propagated deletions of the referenced rows.","solutions":["Restore or re-create the missing related rows in the target database before replaying","Undo the deletions that removed the referenced rows, then retry the transaction","Re-add the relation links manually between the surviving rows","Before scripted relation writes, verify each referenced row exists (GetBlockValue) and has a non-nil Relation value"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// confirm every related row exists with a relation value before linking\nfor (const relAvID of relationAvIDs) {\n  const av = await getAttrView(relAvID);\n  for (const id of relationBlockIDs) {\n    const row = av.keyValues.flatMap(kv => kv.values).find(v => v.blockID === id);\n    if (!row || !row.relation) throw new Error(`related entry ${id} missing`);\n  }\n}","typeGuard":"function hasRelationValue(row) { return !!row && !!row.relation && Array.isArray(row.relation.blockIDs); }","tryCatchPattern":"try { await undo(); } catch (e) { if (/related database entry \\[.+\\] changed/.test(e.message)) { recreateRelatedRows(); retry(); } else throw e; }","preventionTips":["Do not delete rows referenced by two-way relations while replay/undo is pending","Scripted deletions should first collect inbound relation references","Re-add missing related rows before re-applying relation links"],"tags":["database","relation","missing-row","transaction-replay"],"backgroundTag":"record-not-found","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}