siyuan-note/siyuan · error
encrypted database history is plaintext
Error message
encrypted database history is plaintext
What it means
While reading a bound attribute view's history snapshot, the engine requires that history data belonging to a notebook (boxID non-empty) is ciphertext under the encrypted-notebook format. Finding plaintext bytes where the box enforces encryption means the history file was written by an older/non-encrypting writer or tampered with, so it is rejected instead of silently accepting unauthenticated data.
Solutions
- Do not import plaintext history into an encrypted notebook; regenerate history from the original encrypted workspace
- Verify the history file's boxID matches a notebook that was actually encrypted at write time
- Re-sync or re-create history by triggering a new snapshot of the database
- Restore the original ciphertext file from backup if it was overwritten or corrupted
Defensive patterns
Strategy: validation
Validate before calling
const data = await readFile(filename);
if (boxID && !isCiphertext(data)) {
throw new Error('history file is plaintext; cannot use with encrypted notebook');
} Type guard
function isUsableEncryptedHistory(data, boxID) {
return !boxID || isCiphertext(data);
} Prevention
- Never copy history files from plaintext workspaces into encrypted notebooks
- Keep history directories per-workspace; do not merge them across setups
- Back up encrypted notebooks together with their history and key material
- Upgrade notebooks through the supported migration instead of hand-copying files
When it happens
Trigger: restoreAttributeViewHistory, restoreEmbeddedAttributeViewHistory, or backupBoundAttributeViewHistory reading a history file for an encrypted notebook's database where util.IsCiphertext(data) returns false — e.g. a history file produced before encryption was enabled, copied from a plaintext workspace, or corrupted so the ciphertext header is lost.
Common situations: Restoring database history after migrating notebooks between encrypted and non-encrypted workspaces; mixing history directories across versions; manually editing or syncing history files.
Related errors
- encrypted database history has no notebook context
- CLI does not support encrypted notebook history
- Conf.Language(314)
- Conf.Language(381)
- database back relation changed since document deletion
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/8d5033187c7de2b5.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/attribute_view_block_history.go:29
"github.com/88250/lute/ast"
"github.com/88250/lute/parse"
"github.com/siyuan-note/filelock"
"github.com/siyuan-note/siyuan/kernel/av"
"github.com/siyuan-note/siyuan/kernel/util"
)
func boundAttributeViewHistoryPath(historyDir, boxID, avID string) string {
return filepath.Join(historyDir, boxID, "storage", "av", avID+".json")
}
func readBoundAttributeViewHistory(filename, boxID, avID string) (*av.AttributeView, error) {
data, err := filelock.ReadFile(filename)
if err != nil {
return nil, err
}
if boxID != "" {
if !util.IsCiphertext(data) {
return nil, errors.New("encrypted database history is plaintext")
}
data, err = av.DecryptAVData(boxID, avID, data)
if err != nil {
return nil, err
}
} else if util.IsCiphertext(data) {
return nil, errors.New("encrypted database history has no notebook context")
}
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")View on GitHub (pinned to 9f775e8a12)