siyuan-note/siyuan · error

Conf.Language(387)

Error message

Conf.Language(387)

What it means

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.

Solutions

  1. Close any program holding files in the notebook folder (editors, file previews, antivirus scans), then retry unmount/remove
  2. Check that data/<notebook>/ and its contents are writable (on Windows remove read-only flags; on Unix check permissions/ownership)
  3. Pause third-party sync/backup tools watching the workspace, delete the residual files listed in the error manually, and retry
Defensive patterns

Strategy: try-catch

Validate before calling

async function canWriteDir(dir) {
  try { const probe = dir + "/.probe"; await writeTextFile(probe, "x"); await removeFile(probe); return true; }
  catch { return false; }
}

Try / catch

try { await removeNotebook(boxID); } catch (e) { if (isLangMsg(e, 387)) { showTip("Close apps locking the notebook folder, delete residual files, then retry"); } }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/8cd607e01043a46b. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/mount.go:230

			}
			return nil
		}); nil != walkErr {
			logging.LogWarnf("list residual entries of [%s] failed: %s", p, walkErr)
		}
		hasResidual := 0 < len(residual)

		lastAttempt := 4 == i
		// 先清空残留内容(深路径优先),下一轮重试再删除目录本身,避免 RemoveAll 再次卡在"目录非空"
		sort.Sort(sort.Reverse(sort.StringSlice(residual)))
		for _, rel := range residual {
			if removeErr := filelock.RemoveWithoutFatal(filepath.Join(p, rel)); nil != removeErr {
				logging.LogWarnf("remove residual entry [%s] failed: %s", rel, removeErr)
			}
		}

		if lastAttempt {
			if hasResidual {
				err = fmt.Errorf(Conf.Language(387), p, strings.Join(describeResidualEntries(residual), ", "))
			} else {
				err = fmt.Errorf(Conf.Language(387), p, err.Error())
			}
			return
		}

		time.Sleep(100 * time.Millisecond)
	}
	return
}

// describeResidualEntries 返回仍然存在的残留条目描述(含目录),最多 3 项。
func describeResidualEntries(entries []string) (ret []string) {
	sort.Strings(entries)
	for _, entry := range entries {
		if 3 == len(ret) {
			ret = append(ret, "...")
			break

View on GitHub (pinned to 9f775e8a12)