siyuan-note/siyuan · error
The notebook already exists
Error message
The notebook already exists
What it means
Thrown by RollbackNotebookHistory when filelock.IsExist(to) is true, meaning a notebook directory with the target boxID already exists on disk at filepath.Join(util.DataDir, boxID). Notebook history rollback restores a deleted notebook by copying its history directory back to the data directory. If a notebook with the same ID already exists, the restore would overwrite or conflict with live data. The error message is Conf.Language(371) = "The notebook already exists".
Source
Thrown at kernel/model/history.go:617
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 251596fc0d)
Solutions
- Delete or rename the existing notebook directory at data/<boxID> before attempting the history rollback restore.
- If the existing notebook is the one you want, no rollback is needed — the notebook is already present.
- If the existing notebook is a different notebook that collided on ID, rename its directory first, then retry the rollback.
Defensive patterns
Strategy: validation
Validate before calling
// Check if the target notebook directory already exists before rollback
const targetPath = `${dataDir}/${boxID}`
if (await pathExists(targetPath)) {
// Notebook already exists — do not rollback, or remove the existing dir first
throw new Error('Notebook already exists at target path')
}
await post('/api/history/rollbackNotebookHistory', { historyPath }) Prevention
- Check if a notebook with the target ID already exists before attempting history restore.
- If the notebook exists and is different, rename or remove it before rollback.
- Avoid re-creating notebooks with IDs of deleted notebooks if you plan to restore from history.
When it happens
Trigger: Calling POST /api/history/rollbackNotebookHistory to restore a notebook from deletion history, but a notebook with the same ID already exists in the data directory. This happens when the user re-created a notebook with the same ID after deleting the original, or the notebook was not actually deleted (the history is from a different delete event).
Common situations: User deleted a notebook, created a new notebook that happened to get the same ID (or manually reused the ID), then tries to restore the old notebook from history; the deletion history is stale because the notebook was restored by another means already; filesystem has residual data from a partially completed previous operation.
Related errors
- template already exists, use --overwrite to replace: %s
- copy unused asset [%s] to history failed: %w
- remove unused asset [%s] failed: %w
- prepare box conf [%s] failed: %w
- mkdir box conf dir failed: %w
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/16ea0a24b13ae029.
Report an issue: GitHub.