siyuan-note/siyuan · error

encrypted notebook snapshot document is plaintext [%s]

Error message

encrypted notebook snapshot document is plaintext [%s]

What it means

The snapshot document belongs to an encrypted notebook (IsEncryptedBox) but its stored bytes are plaintext. The kernel refuses to read unencrypted content from an encrypted notebook's snapshot store, preserving the guarantee that encrypted notebooks never expose plaintext data at rest.

Source

Thrown at kernel/model/history_diff.go:437

		return nil, err
	}
	ciphertext := util.IsCiphertext(data)
	if ciphertext {
		if len(pathParts) < 2 || !ast.IsNodeIDPattern(pathParts[0]) || !IsEncryptedBox(pathParts[0]) {
			return nil, errors.New("encrypted snapshot document is missing valid notebook context")
		}
		HoldBoxReadLock(pathParts[0])
		defer ReleaseBoxReadLock(pathParts[0])
		dek, unlockErr := GetDEKIfUnlocked(pathParts[0])
		if unlockErr != nil {
			return nil, errors.New(Conf.Language(314))
		}
		data, err = DecryptFile(pathParts[0], pathParts[1], dek, data)
		if err != nil {
			return nil, err
		}
	} else if len(pathParts) > 0 && IsEncryptedBox(pathParts[0]) {
		return nil, fmt.Errorf("encrypted notebook snapshot document is plaintext [%s]", pathParts[0])
	}
	rootID := strings.TrimSuffix(filepath.Base(file.Path), filepath.Ext(file.Path))
	tree, err := parseDocVersionTree(data, rootID)
	boxID := ""
	if 0 < len(pathParts) && ast.IsNodeIDPattern(pathParts[0]) {
		boxID = pathParts[0]
	}
	if err != nil {
		return &loadedDocVersion{
			title:    rootID,
			rootID:   rootID,
			raw:      data,
			parseErr: err,
			large:    1024*1024 <= len(data),
			boxID:    boxID,
		}, nil
	}
	return &loadedDocVersion{

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Re-snapshot the document so it is encrypted under the notebook's DEK
  2. Remove the plaintext snapshot and rebuild the snapshot index
  3. Verify the notebook's encrypted status matches how its data is actually stored
Defensive patterns

Strategy: validation

Validate before calling

if (isEncryptedBox(boxID) && !util.isCiphertext(data)) throw new Error("snapshot must be ciphertext");

Try / catch

try { await api.loadDocVersion(fileID); } catch (e) { if (String(e).includes("is plaintext")) { showToast("Snapshot is corrupt; rebuild snapshots for this notebook"); } }

Prevention

When it happens

Trigger: Opening a snapshot for an encrypted notebook where the repo-stored .sy content is unencrypted — written by a non-encrypting path or manually uploaded from a plain notebook.

Common situations: Migrating a plain notebook into encrypted storage without re-encrypting snapshots; a bug in snapshot write paths; manually injecting plaintext files into the repo store.

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/bf9d63b495f45126. Report an issue: GitHub.