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

  1. Delete or rename the existing notebook directory at data/<boxID> before attempting the history rollback restore.
  2. If the existing notebook is the one you want, no rollback is needed — the notebook is already present.
  3. 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

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


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/16ea0a24b13ae029. Report an issue: GitHub.