siyuan-note/siyuan · error
Conf.Language(371)
Error message
Conf.Language(371)
What it means
RollbackNotebookHistory restores a deleted notebook from history to data/<boxID>. Before copying, it checks whether the target directory already exists (filelock.IsExist) and aborts with the localized message Conf.Language(371) to avoid overwriting or merging with a live notebook that now occupies the same ID.
Source
Thrown at kernel/model/history.go:644
func RollbackNotebookHistory(historyPath string) (err error) {
historyPath, err = validateHistoryPath(historyPath)
if err != nil {
return
}
boxID, err := validateNotebookHistoryPath(historyPath)
if err != nil {
return
}
if _, loaded := boxLock.LoadOrStore(boxID, true); loaded {
return errors.New(Conf.Language(239))
}
defer boxLock.Delete(boxID)
from := historyPath
to := filepath.Join(util.DataDir, boxID)
if filelock.IsExist(to) {
return errors.New(Conf.Language(371))
}
if err = filelock.CopyNewtimes(from, to); err != nil {
logging.LogErrorf("copy file [%s] to [%s] failed: %s", from, to, err)
return
}
IncSync()
ReloadFiletree()
util.PushMsg(Conf.Language(372), 3000)
return nil
}
func validateNotebookHistoryPath(historyPath string) (boxID string, err error) {
rel, err := filepath.Rel(util.HistoryDir, historyPath)
if err != nil {
return "", fmt.Errorf("invalid notebook history path [%s]", historyPath)
}View on GitHub (pinned to 8641553a1f)
Solutions
- Open the existing notebook to confirm whether the restore is still needed; if it is already restored, skip the rollback
- Move/rename the existing data/<boxID> directory out of the workspace if you intentionally want to replace it with the historical version, then roll back
- Roll back to a different notebook by first clearing the conflicting data — do not attempt to merge
- Refresh the history list; the deletion entry is likely stale
Example fix
// before: blind retry keeps failing
rollbackNotebookHistory(historyPath);
// after: check target first and clear intentionally
if (exists(workspaceDir + "/data/<boxID>")) {
rename(workspaceDir + "/data/<boxID>", backupDir + "/<boxID>.bak");
}
rollbackNotebookHistory(historyPath); Defensive patterns
Strategy: validation
Validate before calling
async function notebookAlreadyExists(boxID) {
const r = await fetchPost("/api/filetree/checkFileExist", {path: "/data/" + boxID});
return r.code === 0 && r.data.isExist === true;
} Type guard
null
Try / catch
try { await rollbackNotebookHistory(p); } catch (e) { if (isNotebookExistsMessage(e)) { /* prompt user: notebook already exists, skip or replace manually */ } else { throw e; } } Prevention
- Check whether the notebook was re-created before attempting rollback
- Do not reuse notebook IDs when re-importing deleted notebooks
- Refresh the history list so stale delete entries are not re-rolled
When it happens
Trigger: Calling rollbackNotebookHistory for a notebook whose ID directory already exists in the workspace — e.g. the notebook was recreated after deletion (new data under the same ID), the deletion history is old, or a sync brought the notebook back before the rollback ran.
Common situations: User deleted a notebook, then re-imported/re-created it, then tried to restore the old snapshot; duplicate ID collisions after copying notebooks between workspaces; sync resolving a deletion independently on another device; stale history panel entry for an already-restored notebook.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- can not get or create rollback box
- Conf.Language(239)
- invalid notebook history path [%s]
- encrypted document history is missing valid notebook context
- encrypted notebook document history is plaintext [%s]
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/99de803579149303.
Report an issue: GitHub.