siyuan-note/siyuan · error
Conf.Language(239)
Error message
Conf.Language(239)
What it means
RollbackNotebookHistory takes a per-notebook lock (boxLock.LoadOrStore) to prevent two concurrent rollbacks of the same notebook. If the box is already locked — another rollback of that notebook is in flight — the call fails with the localized message Conf.Language(239) instead of corrupting data with overlapping restores.
Source
Thrown at kernel/model/history.go:637
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 nilView on GitHub (pinned to 8641553a1f)
Solutions
- Wait for the in-flight rollback to finish; the lock is removed (boxLock.Delete) when the first call completes
- Disable the rollback button while a request is pending (single-flight on the client)
- Serialize rollback requests per notebook in automation scripts (await previous before next)
- Retry the request after a short delay if it was rejected due to the concurrent guard
Example fix
// before: fire-and-forget, allows double submit
rollbackNotebookHistory(path); rollbackNotebookHistory(path);
// after: guard against re-entry
let busy = false;
async function rollbackOnce(p) {
if (busy) return;
busy = true;
try { await rollbackNotebookHistory(p); } finally { busy = false; }
} Defensive patterns
Strategy: try-catch
Validate before calling
const inFlight = new Set();
function canStartNotebookRollback(boxID) { return !inFlight.has(boxID); } Type guard
null
Try / catch
try { await rollbackNotebookHistory(p); } catch (e) { if (isI18nBusyMessage(e)) { await sleep(1000); /* retry once */ } else { throw e; } } Prevention
- Disable the rollback button while a request is pending
- Serialize rollbacks per notebook in scripts and plugins
- Avoid triggering the same rollback from multiple windows/devices at once
When it happens
Trigger: Two simultaneous calls to rollbackNotebookHistory (API /api/history/rollbackNotebookHistory) for the same notebook ID, e.g. double-clicking the rollback button or triggering rollback from two clients while the first copy operation is still running.
Common situations: Impatient users clicking rollback repeatedly; automation scripts firing overlapping requests; multi-window/multi-device sessions issuing the same rollback; slow copy of a large notebook holding the lock for a long time.
Related errors
- 239
- Conf.Language(371)
- can not get or create rollback box
- install local marketplace package failed: %w; rollback faile
- Related operations are being processed, please try again lat
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/ba831f86f07c87a8.
Report an issue: GitHub.