siyuan-note/siyuan · warning

Related operations are being processed, please try again lat

Error message

Related operations are being processed, please try again later

What it means

Thrown by RollbackNotebookHistory when boxLock.LoadOrStore(boxID, true) returns loaded=true, indicating another operation is already holding the lock for this notebook ID. The boxLock is a sync.Map used to serialize concurrent notebook-level operations. The error message is Conf.Language(239) = "Related operations are being processed, please try again later". This is a transient concurrency guard, not a permanent failure.

Source

Thrown at kernel/model/history.go:610

	encrypted, err := isEncryptedHistoryBoxDir(filepath.Join(util.HistoryDir, parts[0], boxID))
	if err != nil {
		logging.LogErrorf("inspect encrypted history path [%s] failed: %s", absPath, err)
		return true
	}
	return encrypted
}

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

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Wait a moment and retry the rollback — the lock is released when the in-flight operation completes (typically within seconds).
  2. Disable the rollback button in the UI immediately after click and re-enable it on response to prevent duplicate submissions.
  3. As an API client, implement exponential backoff retry on this specific error (it is transient).

Example fix

// before
await post('/api/history/rollbackNotebookHistory', { historyPath })

// after — retry with backoff on the "operations being processed" error
async function rollbackWithRetry(historyPath, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await post('/api/history/rollbackNotebookHistory', { historyPath })
    } catch (e) {
      if (e.code === 239 && i < maxRetries - 1) {
        await sleep(500 * (i + 1))
        continue
      }
      throw e
    }
  }
}
Defensive patterns

Strategy: retry

Try / catch

// Retry with backoff on the concurrent-operation lock error
async function rollbackNotebookHistorySafe(historyPath, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await post('/api/history/rollbackNotebookHistory', { historyPath })
    } catch (e) {
      if (e.code === 239 && i < maxRetries - 1) {
        await sleep(500 * (i + 1))
        continue
      }
      throw e
    }
  }
}

Prevention

When it happens

Trigger: Calling POST /api/history/rollbackNotebookHistory while another rollback, file operation, or sync is already in progress for the same notebook ID. The lock is acquired per-notebook, so only same-notebook operations conflict. The lock is released (defer boxLock.Delete) when the first operation completes.

Common situations: User double-clicks the rollback button rapidly; two browser tabs initiate rollback on the same notebook simultaneously; a sync operation holds the lock while the user triggers rollback; an API client retries too aggressively.

Related errors


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