{"record":{"id":"82ca04e0265dc31d","repo":"siyuan-note/siyuan","slug":"database-history-has-no-primary-field","errorCode":null,"errorMessage":"database history has no primary field","messagePattern":"database history has no primary field","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_block_history.go","lineNumber":56,"sourceCode":"\tview, err := av.ParseAttributeViewData(avID, data)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif view.ID != avID {\n\t\treturn nil, errors.New(\"database history ID does not match its filename\")\n\t}\n\tfor _, kv := range view.KeyValues {\n\t\tif kv == nil || kv.Key == nil {\n\t\t\treturn nil, errors.New(\"database history contains an invalid field\")\n\t\t}\n\t\tfor _, value := range kv.Values {\n\t\t\tif value == nil {\n\t\t\t\treturn nil, errors.New(\"database history contains an invalid value\")\n\t\t\t}\n\t\t}\n\t}\n\tif view.GetBlockKeyValues() == nil {\n\t\treturn nil, errors.New(\"database history has no primary field\")\n\t}\n\tfor _, layout := range view.Views {\n\t\tif layout == nil {\n\t\t\treturn nil, errors.New(\"database history contains an invalid view\")\n\t\t}\n\t\tfor _, group := range layout.Groups {\n\t\t\tif group == nil {\n\t\t\t\treturn nil, errors.New(\"database history contains an invalid group\")\n\t\t\t}\n\t\t}\n\t}\n\treturn view, nil\n}\n\n// 内嵌数据库也先认证历史，并与绑定条目恢复一起纳入失败回滚。\nfunc (tx *Transaction) restoreEmbeddedAttributeViewHistory(filename, boxID, avID string) error {\n\thistorical, err := readBoundAttributeViewHistory(filename, boxID, avID)\n\tif err != nil {","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_block_history.go#L38-L74","documentation":"readBoundAttributeViewHistory validates a historical snapshot of an attribute view (database) before it is backed up or merged back during document history restore/rollback. The snapshot must contain a primary (block-type) key field, retrieved via GetBlockKeyValues(); without it, no bound block entries can be identified or restored. The kernel throws this error to fail fast on a structurally incomplete or corrupted database history file rather than silently performing a partial restore.","triggerScenarios":"RollbackDocHistory, restoreEmbeddedAttributeViewHistory, or backupBoundAttributeViewHistory reads a historical attribute-view JSON (storage/av/<avID>.json snapshot) whose KeyValues contain no KeyTypeBlock field — e.g. a hand-edited, truncated, or old-format history file, or a snapshot of a database whose block/primary key was deleted before deletion backup.","commonSituations":"Restoring a document whose database history snapshot predates schema changes or was produced by an older SiYuan version; corrupted or partially written history files after a crash; manually copying history files between workspaces or notebooks (encrypted vs plaintext mismatch handled separately); databases converted to detached-item-only layouts.","solutions":["Delete the offending snapshot under <historyDir>/<boxID>/storage/av/<avID>.json (or pick a different history version) so the restore skips the corrupt snapshot, then retry the rollback","Verify the snapshot JSON contains a KeyValues entry whose Key.Type is \"block\"; if it was hand-edited, restore the primary field","Check whether the database itself still has its primary key in the live workspace; if only the history is malformed, regenerate history by re-deleting after fixing the database","Report/upgrade if produced by an older version — old history formats may lack the block key; use a history taken by the current version"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Go: before relying on a history snapshot, check it has a primary field\nfunc hasPrimaryField(data []byte) error {\n    view, err := av.ParseAttributeViewData(avID, data)\n    if err != nil { return err }\n    if view.GetBlockKeyValues() == nil {\n        return errors.New(\"snapshot has no primary (block) field\")\n    }\n    return nil\n}","typeGuard":"if view.GetBlockKeyValues() == nil {\n    // handle missing primary field before use\n    return ErrNoPrimaryField\n}","tryCatchPattern":"view, err := readBoundAttributeViewHistory(file, boxID, avID)\nif err != nil {\n    if strings.Contains(err.Error(), \"no primary field\") {\n        // skip this snapshot / pick another history version\n        return nil\n    }\n    return err\n}","preventionTips":["Never hand-edit historical storage/av/*.json snapshots","Keep the block (primary) key when restructuring a database","Regenerate history backups after schema migrations","Test rollback on a copy of the workspace before production use"],"tags":["database","history","attribute-view","validation"],"backgroundTag":"schema-validation-failed","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"}