siyuan-note/siyuan · error

encrypted document history is missing valid notebook context

Error message

encrypted document history is missing valid notebook context

What it means

When a history .sy file's bytes are detected as ciphertext, the path must contain a valid notebook (box) ID segment that identifies an encrypted notebook, so the file can be decrypted with that notebook's DEK. This error means the ciphertext history file cannot be associated with an encrypted notebook from its path (missing path segments, malformed notebook ID, or notebook not registered as encrypted).

Source

Thrown at kernel/model/history_diff.go:352

	if err != nil {
		return nil, err
	}
	if !strings.HasSuffix(strings.ToLower(absPath), ".sy") {
		return nil, errors.New("history version is not a document")
	}
	relPath, err := filepath.Rel(util.HistoryDir, absPath)
	if err != nil {
		return nil, err
	}
	parts := strings.SplitN(filepath.ToSlash(relPath), "/", 3)
	data, err := filelock.ReadFile(absPath)
	if err != nil {
		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])

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure the history path is under history/<timestamp>/<boxID>/<docID>.sy with a valid encrypted notebook ID
  2. Confirm the notebook exists and is registered as an encrypted box (IsEncryptedBox)
  3. Regenerate or re-import the history into the correct notebook directory

Example fix

// before
loadDocVersion("history/20240101/orphan.sy")
// after
loadDocVersion("history/20240101/20240101120000-boxid/doc.sy")
Defensive patterns

Strategy: validation

Validate before calling

const parts = relPath.split("/"); if (parts.length < 3 || !isNodeID(parts[1]) || !isEncryptedBox(parts[1])) throw new Error("no valid encrypted notebook context");

Type guard

const hasEncBoxContext = (parts: string[]) => parts.length >= 3 && /^[0-9]{14}-[0-9a-z]{7}$/.test(parts[1]);

Try / catch

try { await api.loadDocVersion(path); } catch (e) { if (String(e).includes("missing valid notebook context")) { showToast("History entry cannot be tied to its encrypted notebook"); } }

Prevention

When it happens

Trigger: Loading a ciphertext history file whose path has fewer than 3 segments below HistoryDir, whose second segment is not a node-ID-pattern notebook ID, or whose notebook is not flagged as an encrypted box.

Common situations: Ciphertext history files copied/moved outside their notebook directory; a notebook removed from the encrypted-box list after history was written; manual reconstruction of history paths; restoring history data without its notebook context.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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