{"record":{"id":"a0224ab1a7cd025b","repo":"siyuan-note/siyuan","slug":"related-operations-are-being-processed-please-try-a0224a","errorCode":null,"errorMessage":"Related operations are being processed, please try again later","messagePattern":"Related operations are being processed, please try again later","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/history.go","lineNumber":610,"sourceCode":"\tencrypted, err := isEncryptedHistoryBoxDir(filepath.Join(util.HistoryDir, parts[0], boxID))\n\tif err != nil {\n\t\tlogging.LogErrorf(\"inspect encrypted history path [%s] failed: %s\", absPath, err)\n\t\treturn true\n\t}\n\treturn encrypted\n}\n\nfunc RollbackNotebookHistory(historyPath string) (err error) {\n\thistoryPath, err = validateHistoryPath(historyPath)\n\tif err != nil {\n\t\treturn\n\t}\n\tboxID, err := validateNotebookHistoryPath(historyPath)\n\tif err != nil {\n\t\treturn\n\t}\n\tif _, loaded := boxLock.LoadOrStore(boxID, true); loaded {\n\t\treturn errors.New(Conf.Language(239))\n\t}\n\tdefer boxLock.Delete(boxID)\n\n\tfrom := historyPath\n\tto := filepath.Join(util.DataDir, boxID)\n\tif filelock.IsExist(to) {\n\t\treturn errors.New(Conf.Language(371))\n\t}\n\n\tif err = filelock.CopyNewtimes(from, to); err != nil {\n\t\tlogging.LogErrorf(\"copy file [%s] to [%s] failed: %s\", from, to, err)\n\t\treturn\n\t}\n\n\tIncSync()\n\tReloadFiletree()\n\tutil.PushMsg(Conf.Language(372), 3000)\n\treturn nil","sourceCodeStart":592,"sourceCodeEnd":628,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/history.go#L592-L628","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wait a moment and retry the rollback — the lock is released when the in-flight operation completes (typically within seconds).","Disable the rollback button in the UI immediately after click and re-enable it on response to prevent duplicate submissions.","As an API client, implement exponential backoff retry on this specific error (it is transient)."],"exampleFix":"// before\nawait post('/api/history/rollbackNotebookHistory', { historyPath })\n\n// after — retry with backoff on the \"operations being processed\" error\nasync function rollbackWithRetry(historyPath, maxRetries = 3) {\n  for (let i = 0; i < maxRetries; i++) {\n    try {\n      return await post('/api/history/rollbackNotebookHistory', { historyPath })\n    } catch (e) {\n      if (e.code === 239 && i < maxRetries - 1) {\n        await sleep(500 * (i + 1))\n        continue\n      }\n      throw e\n    }\n  }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Retry with backoff on the concurrent-operation lock error\nasync function rollbackNotebookHistorySafe(historyPath, maxRetries = 3) {\n  for (let i = 0; i < maxRetries; i++) {\n    try {\n      return await post('/api/history/rollbackNotebookHistory', { historyPath })\n    } catch (e) {\n      if (e.code === 239 && i < maxRetries - 1) {\n        await sleep(500 * (i + 1))\n        continue\n      }\n      throw e\n    }\n  }\n}","preventionTips":["Disable the rollback button immediately after click to prevent duplicate submissions.","Implement client-side debounce on rollback triggers.","Use exponential backoff retry for this transient concurrency error."],"tags":["history","rollback","concurrency","lock","transient","retry"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}