{"record":{"id":"7e8ce81b9f7d6b8b","repo":"siyuan-note/siyuan","slug":"conf-language-222","errorCode":null,"errorMessage":"Conf.Language(222)","messagePattern":"Conf\\.Language\\(222\\)","errorType":"validation","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/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/shortcuts.go#L29-L65","documentation":"MoveLocalShorthands migrates locally captured shorthand (quick-capture) files into a notebook. It first tries a non-blocking acquire of the global sync lock (syncLock.TryLock); when sync/other consumers already hold the lock it aborts with the localized message Conf.Language(222) (\"Please try again later\" — sync in progress) instead of waiting or racing with synchronization.","triggerScenarios":"Calling MoveLocalShorthands (directly or via moveLocalShorthands/consumeShorthands) while data sync is running or another goroutine holds syncLock — e.g. quick capture consumption triggered concurrently with an auto-sync cycle.","commonSituations":"Auto sync kicks in right when the user opens the app and shorthands are consumed; a long sync (large initial upload) overlaps with shorthand movement; tests running multiple MoveLocalShorthands concurrently.","solutions":["Retry after the sync finishes — the kernel reports this condition to the UI, which will retry on the next trigger","Manually run sync first, wait for completion, then trigger shorthand consumption","In scripts/tests, serialize calls or wait for syncLock availability before invoking"],"exampleFix":"null","handlingStrategy":"retry","validationCode":"// Probe lock availability before the call (best effort)\nif !syncLock.TryLock() { return errSyncBusy }; syncLock.Unlock()","typeGuard":null,"tryCatchPattern":"retIDs, err := MoveLocalShorthands(boxID)\nif err != nil && strings.Contains(err.Error(), Conf.Language(222)) {\n    time.Sleep(5 * time.Second)\n    retIDs, err = MoveLocalShorthands(boxID) // retry after sync completes\n}","preventionTips":["Avoid triggering shorthand consumption while a sync is visibly running","Serialize shorthand movement with sync scheduling in custom automation","Treat Language(222) as transient and surface a retry-able status to users"],"tags":["sync-lock","shorthands","concurrency","busy-resource"],"backgroundTag":"conflicting-config-options","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}