siyuan-note/siyuan · error
cannot remove storage root
Error message
cannot remove storage root
What it means
siyuan.storage.remove resolves the given path inside the plugin's storage directory and refuses to delete the storage root itself (abs == p.storageDir). Deleting the root would wipe all plugin storage, so the API rejects with 'cannot remove storage root'.
Solutions
- Pass a path that points to a specific file or subdirectory, not the root
- Guard the call: skip removal when the path is empty or resolves to the root
- To clear all data, iterate siyuan.storage.list('/') and remove each entry individually instead of removing the root
Example fix
// before
await siyuan.storage.remove(path || "/");
// after
if (path && path !== "/") {
await siyuan.storage.remove(path);
} Defensive patterns
Strategy: validation
Validate before calling
function canRemove(p) {
const norm = "/" + String(p ?? "").replace(/^\/+/, "");
return norm !== "/" && norm.length > 1;
}
if (!canRemove(path)) throw new Error("refusing to remove storage root"); Try / catch
try {
await siyuan.storage.remove(path);
} catch (e) {
if (String(e).includes("storage root")) {
console.warn("path resolved to storage root; skipped");
} else throw e;
} Prevention
- Never pass empty strings or '/' to storage.remove
- Guard default parameter values that may be empty
- To wipe data, enumerate entries with list and delete them individually
When it happens
Trigger: Calling siyuan.storage.remove("") or siyuan.storage.remove("/") or any path that resolves exactly to the plugin storage directory root. Path resolution normalizes the input, so any spelling that lands on the root triggers this.
Common situations: Plugin cleanup code that computes a path from an empty variable or default; calling remove with no meaningful segment; a loop that deletes all entries and also targets the root.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- asset path must be under assets
- Conf.Language(142)
- config.actionEffects contains an empty action
- custom emoji name must not be empty
- encrypted attribute view history is missing valid notebook…
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/39bcc3722f398739.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/plugin/api_storage.go:334
}
runErr := p.worker.Run(func(rt *goja.Runtime) (result any, err error) {
if argErr != nil {
err = argErr
return
}
if util.ReadOnly {
err = fmt.Errorf("The current kernel is in read-only mode, storage.remove is not allowed")
return
}
abs, resolveErr := resolvePath(path)
if resolveErr != nil {
err = resolveErr
return
}
if abs == p.storageDir {
err = fmt.Errorf("cannot remove storage root")
return
}
go func() (result any, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic during siyuan.storage.remove: %v", r)
}
p.worker.Run(func(rt *goja.Runtime) (_ any, _ error) {
if lo.IsNil(err) {
if resolveErr := resolve(result); resolveErr != nil {
logging.LogErrorf("[plugin:%s] siyuan.storage.remove resolve: %v", p.Name, resolveErr)
}
} else {
if rejectErr := reject(rt.NewGoError(err)); rejectErr != nil {
logging.LogErrorf("[plugin:%s] siyuan.storage.remove reject: %v", p.Name, rejectErr)
}View on GitHub (pinned to 9f775e8a12)