siyuan-note/siyuan · error
database history has no primary field
Error message
database history has no primary field
What it means
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.
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
Defensive patterns
Strategy: validation
Validate before calling
// Go: before relying on a history snapshot, check it has a primary field
func hasPrimaryField(data []byte) error {
view, err := av.ParseAttributeViewData(avID, data)
if err != nil { return err }
if view.GetBlockKeyValues() == nil {
return errors.New("snapshot has no primary (block) field")
}
return nil
} Type guard
if view.GetBlockKeyValues() == nil {
// handle missing primary field before use
return ErrNoPrimaryField
} Try / catch
view, err := readBoundAttributeViewHistory(file, boxID, avID)
if err != nil {
if strings.Contains(err.Error(), "no primary field") {
// skip this snapshot / pick another history version
return nil
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- database history contains an invalid group
- database history contains an invalid view
- cannot remove primary key field
- database back relation changed since document deletion
- database back relation value is invalid
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/82ca04e0265dc31d.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/attribute_view_block_history.go:56
view, err := av.ParseAttributeViewData(avID, data)
if err != nil {
return nil, err
}
if view.ID != avID {
return nil, errors.New("database history ID does not match its filename")
}
for _, kv := range view.KeyValues {
if kv == nil || kv.Key == nil {
return nil, errors.New("database history contains an invalid field")
}
for _, value := range kv.Values {
if value == nil {
return nil, errors.New("database history contains an invalid value")
}
}
}
if view.GetBlockKeyValues() == nil {
return nil, errors.New("database history has no primary field")
}
for _, layout := range view.Views {
if layout == nil {
return nil, errors.New("database history contains an invalid view")
}
for _, group := range layout.Groups {
if group == nil {
return nil, errors.New("database history contains an invalid group")
}
}
}
return view, nil
}
// 内嵌数据库也先认证历史,并与绑定条目恢复一起纳入失败回滚。
func (tx *Transaction) restoreEmbeddedAttributeViewHistory(filename, boxID, avID string) error {
historical, err := readBoundAttributeViewHistory(filename, boxID, avID)
if err != nil {View on GitHub (pinned to 9f775e8a12)