{"record":{"id":"8cd607e01043a46b","repo":"siyuan-note/siyuan","slug":"conf-language-387","errorCode":null,"errorMessage":"Conf.Language(387)","messagePattern":"Conf\\.Language\\(387\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/mount.go","lineNumber":230,"sourceCode":"\t\t\t}\n\t\t\treturn nil\n\t\t}); nil != walkErr {\n\t\t\tlogging.LogWarnf(\"list residual entries of [%s] failed: %s\", p, walkErr)\n\t\t}\n\t\thasResidual := 0 < len(residual)\n\n\t\tlastAttempt := 4 == i\n\t\t// 先清空残留内容（深路径优先），下一轮重试再删除目录本身，避免 RemoveAll 再次卡在\"目录非空\"\n\t\tsort.Sort(sort.Reverse(sort.StringSlice(residual)))\n\t\tfor _, rel := range residual {\n\t\t\tif removeErr := filelock.RemoveWithoutFatal(filepath.Join(p, rel)); nil != removeErr {\n\t\t\t\tlogging.LogWarnf(\"remove residual entry [%s] failed: %s\", rel, removeErr)\n\t\t\t}\n\t\t}\n\n\t\tif lastAttempt {\n\t\t\tif hasResidual {\n\t\t\t\terr = fmt.Errorf(Conf.Language(387), p, strings.Join(describeResidualEntries(residual), \", \"))\n\t\t\t} else {\n\t\t\t\terr = fmt.Errorf(Conf.Language(387), p, err.Error())\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\ttime.Sleep(100 * time.Millisecond)\n\t}\n\treturn\n}\n\n// describeResidualEntries 返回仍然存在的残留条目描述（含目录），最多 3 项。\nfunc describeResidualEntries(entries []string) (ret []string) {\n\tsort.Strings(entries)\n\tfor _, entry := range entries {\n\t\tif 3 == len(ret) {\n\t\t\tret = append(ret, \"...\")\n\t\t\tbreak","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/mount.go#L212-L248","documentation":"removeBoxDir retries deleting the notebook directory (e.g. during unmount or sync-cleanup); if the final attempt still fails, it returns the localized message for language key 387, formatted with the path and either the residual-entry description (files that could not be removed) or the last error text. It signals that the notebook directory could not be fully removed from disk.","triggerScenarios":"Unmounting/removing a notebook (RemoveBox, mountBox, or cleanupSyncedBoxResiduals) while files under data/<boxID>/ are locked or unwritable — an editor/index process holding handles, read-only filesystem, or sync leaving residual entries that cannot be deleted.","commonSituations":"On Windows: files opened by another program (preview tools, antivirus, cloud-sync clients) locking .sy assets; permission problems after restoring from backup; leftover residual entries after a partially failed sync.","solutions":["Close any program holding files in the notebook folder (editors, file previews, antivirus scans), then retry unmount/remove","Check that data/<notebook>/ and its contents are writable (on Windows remove read-only flags; on Unix check permissions/ownership)","Pause third-party sync/backup tools watching the workspace, delete the residual files listed in the error manually, and retry"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"async function canWriteDir(dir) {\n  try { const probe = dir + \"/.probe\"; await writeTextFile(probe, \"x\"); await removeFile(probe); return true; }\n  catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try { await removeNotebook(boxID); } catch (e) { if (isLangMsg(e, 387)) { showTip(\"Close apps locking the notebook folder, delete residual files, then retry\"); } }","preventionTips":["Close file previews, editors, and cloud-sync clients that open files under data/ before unmounting","Exclude the SiYuan workspace from aggressive antivirus real-time scans","After a failed sync, clear residual entries before retrying notebook removal"],"tags":["filesystem","notebook","file-locked","windows"],"backgroundTag":"file-write-failed","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"}