siyuan-note/siyuan · error

Remove notebook [%s] path [%s] failed: %s

Error message

Remove notebook [%s] path [%s] failed: %s

What it means

In Box.Remove, after validateBoxPath, filelock.Remove(filePath) is attempted; on failure it logs and returns errors.New(fmt.Sprintf(Conf.Language(7), box.Name, path, err)) — 'Remove notebook [%s] path [%s] failed: %s'. filelock.Remove wraps os.Remove, which removes a single file or empty directory and returns an error for a non-empty directory or a missing/locked path.

Source

Thrown at kernel/model/box.go:487

		fromDir := filepath.Join(boxLocalPath, oldDir)
		if util.IsEmptyDir(fromDir) {
			filelock.Remove(fromDir)
		}
	}
	IncSync()
	return nil
}

func (box *Box) Remove(path string) error {
	if _, err := box.validateBoxPath(path); err != nil {
		return err
	}
	boxLocalPath := filepath.Join(util.DataDir, box.ID)
	filePath := filepath.Join(boxLocalPath, path)
	if err := filelock.Remove(filePath); err != nil {
		msg := fmt.Sprintf(Conf.Language(7), box.Name, path, err)
		logging.LogErrorf("remove [path=%s] in box [%s] failed: %s", path, box.ID, err)
		return errors.New(msg)
	}
	IncSync()
	return nil
}

func (box *Box) ListFiles(path string) (ret []*FileInfo) {
	// ListFiles 委托给 Ls,后者已有 validateBoxPath
	fis, _, err := box.Ls(path)
	if err != nil {
		return
	}
	box.listFiles(&fis, &ret)
	return
}

func (box *Box) listFiles(files, ret *[]*FileInfo) {
	for _, file := range *files {
		if file.isdir {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Tolerate 'not exist' as a no-op success (the desired state is already achieved).
  2. To remove a non-empty directory, delete its contents first (walk and remove children), since os.Remove will not recurse.
  3. Close tools holding the file before removing on Windows.
  4. Confirm delete permission on the notebook data dir.

Example fix

// before
if err := box.Remove(path); err != nil { return err }

// after: treat already-gone as success
if err := box.Remove(path); err != nil {
    if errors.Is(err, os.ErrNotExist) { return nil }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Tolerate 'not exist' as the desired end state.
if !box.Exist(path) { return nil }

Try / catch

// Treat already-gone as success; recurse for non-empty dirs.
if err := box.Remove(path); err != nil {
    if errors.Is(err, os.ErrNotExist) { return nil }
    return err
}

Prevention

When it happens

Trigger: POST /api/filetree/removeDoc/removeDocByID/removeDocs (and internal cleanup) removing a path that does not exist, is a non-empty directory (os.Remove rejects those), is locked/open by another process, or lacks delete permission.

Common situations: Removing a folder that still contains .sy files (must empty it first); removing a path already deleted by another client; the .sy is open in an editor/sync process on Windows; read-only volume.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/2f3dfb53ad96ecde. Report an issue: GitHub.