{"record":{"id":"9b7d5bac84432108","repo":"siyuan-note/siyuan","slug":"panic-during-siyuan-storage-remove-v","errorCode":null,"errorMessage":"panic during siyuan.storage.remove: %v","messagePattern":"panic during siyuan\\.storage\\.remove: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_storage.go","lineNumber":341,"sourceCode":"\t\t\tif util.ReadOnly {\n\t\t\t\terr = fmt.Errorf(\"The current kernel is in read-only mode, storage.remove is not allowed\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tabs, resolveErr := resolvePath(path)\n\t\t\tif resolveErr != nil {\n\t\t\t\terr = resolveErr\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif abs == p.storageDir {\n\t\t\t\terr = fmt.Errorf(\"cannot remove storage root\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tgo func() (result any, err error) {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\terr = fmt.Errorf(\"panic during siyuan.storage.remove: %v\", r)\n\t\t\t\t\t}\n\n\t\t\t\t\tp.worker.Run(func(rt *goja.Runtime) (_ any, _ error) {\n\t\t\t\t\t\tif lo.IsNil(err) {\n\t\t\t\t\t\t\tif resolveErr := resolve(result); resolveErr != nil {\n\t\t\t\t\t\t\t\tlogging.LogErrorf(\"[plugin:%s] siyuan.storage.remove resolve: %v\", p.Name, resolveErr)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif rejectErr := reject(rt.NewGoError(err)); rejectErr != nil {\n\t\t\t\t\t\t\t\tlogging.LogErrorf(\"[plugin:%s] siyuan.storage.remove reject: %v\", p.Name, rejectErr)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn\n\t\t\t\t\t}, nil)\n\t\t\t\t}()\n\n\t\t\t\tif removeErr := os.RemoveAll(abs); removeErr != nil {\n\t\t\t\t\terr = fmt.Errorf(\"failed to remove: %w\", removeErr)","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/api_storage.go#L323-L359","documentation":"Thrown when a panic is recovered inside the goroutine that performs the deletion for siyuan.storage.remove(). The deferred recover() wraps the panic value into this message. It is a defensive boundary for unexpected runtime faults during the remove operation, separate from ordinary OS errors which propagate through the worker error path.","triggerScenarios":"A panic inside the remove goroutine — e.g. nil dereference in filelock or os.Remove helpers, or a runtime fault during concurrent plugin teardown.","commonSituations":"Very rare; usually points to a kernel defect or a race during shutdown when the storage watcher is being torn down while a remove is in flight.","solutions":["Inspect the %v detail in the kernel log to locate the panic site.","Avoid issuing remove calls during plugin unload; serialize shutdown ordering.","Retry once to rule out a transient teardown race.","Wrap remove in try/catch and continue if the file is already gone."],"exampleFix":"// before\nawait siyuan.storage.remove(path);\n// after\ntry {\n  await siyuan.storage.remove(path);\n} catch (e) {\n  console.warn('storage.remove failed, assuming already removed', e);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await siyuan.storage.remove(path);\n} catch (e) {\n  if (/panic during siyuan.storage.remove/.test(String(e))) {\n    console.warn('remove panicked, treating as removed', path);\n    return;\n  }\n  throw e;\n}","preventionTips":["Do not issue remove calls during plugin unload to avoid teardown races.","Treat a remove panic as 'already gone' when the file is non-critical."],"tags":["plugin-api","storage","panic","recovery","defensive"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}