siyuan-note/siyuan · error

encrypted notebook document history is plaintext [%s]

Error message

encrypted notebook document history is plaintext [%s]

What it means

The history file belongs to an encrypted notebook (IsEncryptedBox) but its bytes are plaintext, not ciphertext. The kernel requires document history in encrypted notebooks to be stored encrypted; plaintext content there violates the format invariant, so it is rejected instead of silently reading unencrypted data.

Source

Thrown at kernel/model/history_diff.go:365

		return nil, err
	}
	ciphertext := util.IsCiphertext(data)
	if ciphertext {
		if len(parts) < 3 || !ast.IsNodeIDPattern(parts[1]) || !IsEncryptedBox(parts[1]) {
			return nil, errors.New("encrypted document history is missing valid notebook context")
		}
		HoldBoxReadLock(parts[1])
		defer ReleaseBoxReadLock(parts[1])
		dek, dekErr := GetDEKIfUnlocked(parts[1])
		if dekErr != nil {
			return nil, errors.New(Conf.Language(314))
		}
		data, err = DecryptFile(parts[1], parts[2], dek, data)
		if err != nil {
			return nil, err
		}
	} else if len(parts) >= 2 && IsEncryptedBox(parts[1]) {
		return nil, fmt.Errorf("encrypted notebook document history is plaintext [%s]", parts[1])
	}

	rootID := strings.TrimSuffix(filepath.Base(absPath), filepath.Ext(absPath))
	tree, err := parseDocVersionTree(data, rootID)
	historyRoot := filepath.Join(util.HistoryDir, parts[0])
	boxID := ""
	if len(parts) >= 2 && ast.IsNodeIDPattern(parts[1]) {
		boxID = parts[1]
	}
	if err != nil {
		return &loadedDocVersion{
			title:    rootID,
			rootID:   rootID,
			raw:      data,
			parseErr: err,
			large:    1024*1024 <= len(data),
			boxID:    boxID,
			history:  historyRoot,

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Re-generate the history entry so the document is stored encrypted for that notebook
  2. Move the history to a non-encrypted notebook if plaintext storage was intended
  3. Remove the stale plaintext history entry and re-snapshot the document
Defensive patterns

Strategy: validation

Validate before calling

if (isEncryptedBox(boxID) && !util.isCiphertext(data)) throw new Error("expected ciphertext for encrypted notebook");

Try / catch

try { await api.loadDocVersion(path); } catch (e) { if (String(e).includes("is plaintext")) { showToast("History entry is corrupt; re-snapshot the document"); } }

Prevention

When it happens

Trigger: Loading history for an encrypted notebook where the .sy file under history/ is stored unencrypted — e.g. the file was written by a non-encrypting code path or manually copied from a normal notebook.

Common situations: Copying a plain notebook's history into an encrypted notebook's directory; a history-writing bug storing unencrypted data; toggling a notebook to encrypted after history existed in plaintext.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/137463c728e3ae65. Report an issue: GitHub.