{"record":{"id":"0a9b8a667d546f19","repo":"siyuan-note/siyuan","slug":"data-sync-is-in-progress-please-wait-until-data-sync-is","errorCode":null,"errorMessage":"Data sync is in progress, please wait until data sync is completed and try again","messagePattern":"Data sync is in progress, please wait until data sync is completed and try again","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/shortcuts.go","lineNumber":47,"sourceCode":"\t\"sync\"\n\t\"time\"\n\n\t\"github.com/88250/gulu\"\n\t\"github.com/88250/lute/ast\"\n\t\"github.com/88250/lute/parse\"\n\t\"github.com/siyuan-note/logging\"\n\t\"github.com/siyuan-note/siyuan/kernel/cache\"\n\t\"github.com/siyuan-note/siyuan/kernel/treenode\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\nfunc MoveLocalShorthands(boxID string) (retIDs []string, err error) {\n\tshorthandsDir := filepath.Join(util.ShortcutsPath, \"shorthands\")\n\tif !gulu.File.IsDir(shorthandsDir) {\n\t\treturn\n\t}\n\tif !syncLock.TryLock() {\n\t\terr = errors.New(Conf.Language(222))\n\t\treturn\n\t}\n\tdefer syncLock.Unlock()\n\n\tentries, err := os.ReadDir(shorthandsDir)\n\tif nil != err {\n\t\tlogging.LogErrorf(\"read dir [%s] failed: %s\", shorthandsDir, err)\n\t\treturn\n\t}\n\tif !IsShorthandSaveBoxAvailable(boxID) {\n\t\terr = errors.New(Conf.Language(375))\n\t\treturn\n\t}\n\n\tassetsDir := filepath.Join(util.DataDir, \"assets\")\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() && \"assets\" == entry.Name() {\n\t\t\tassetsEntries, readErr := os.ReadDir(filepath.Join(shorthandsDir, entry.Name()))","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/shortcuts.go#L29-L65","documentation":"MoveLocalShorthands moves locally stored shorthand files into a notebook, but it must not run concurrently with data sync because both mutate data files. It tries syncLock.TryLock() and, if a sync is holding the lock, returns the i18n message 222 asking to wait until data sync completes.","triggerScenarios":"Calling MoveLocalShorthands (directly or via the shorthand consumption flow) while model sync operations hold syncLock — e.g. an automatic cloud sync running in the background at the same moment shorthands are processed.","commonSituations":"Startup or timer-driven shorthand consumption racing an auto-sync; user-triggered sync overlapping shorthand import; long syncs on slow networks making the race more likely.","solutions":["Retry MoveLocalShorthands after the sync completes (listen for sync-finished push events)","Schedule shorthand movement for idle periods when no sync is in progress","Check sync status via the sync API before invoking the move"],"exampleFix":"// before\n_, err := model.MoveLocalShorthands(boxID) // may fail during sync\n// after\nfor {\n    retIDs, err := model.MoveLocalShorthands(boxID)\n    if err == nil || !strings.Contains(err.Error(), Conf.Language(222)) {\n        break\n    }\n    time.Sleep(time.Second) // wait for sync to finish, then retry\n}","handlingStrategy":"retry","validationCode":"const status = await api.getSyncState()\nif (status.syncing) { await waitForSyncCompletion(); }","typeGuard":null,"tryCatchPattern":"try { await api.moveLocalShorthands(boxID) } catch (e) { if (String(e).includes('sync is in progress')) { setTimeout(retryMove, 5000); } else throw e }","preventionTips":["Trigger shorthand moves after sync-finished push events, not on fixed timers","Check sync status before invoking data-mutating APIs","Serialize your automation with the app's sync lifecycle instead of parallel timers"],"tags":["sync","lock-contention","siyuan"],"backgroundTag":"operation-in-progress-retry","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}