{"record":{"id":"fbf4568464512dde","repo":"plandex-ai/plandex","slug":"error-deleting-plan-dir","errorCode":null,"errorMessage":"Error deleting plan dir: ","messagePattern":"Error deleting plan dir: ","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/plans_crud.go","lineNumber":258,"sourceCode":"\n\trowsAffected, err := res.RowsAffected()\n\tif err != nil {\n\t\tlog.Printf(\"Error getting rows affected: %v\\n\", err)\n\t\thttp.Error(w, \"Error getting rows affected: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif rowsAffected == 0 {\n\t\tlog.Println(\"Plan not found\")\n\t\thttp.Error(w, \"Not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\n\terr = db.DeletePlanDir(auth.OrgId, planId)\n\n\tif err != nil {\n\t\tlog.Printf(\"Error deleting plan dir: %v\\n\", err)\n\t\thttp.Error(w, \"Error deleting plan dir: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tlog.Println(\"Successfully deleted plan\", planId)\n}\n\nfunc DeleteAllPlansHandler(w http.ResponseWriter, r *http.Request) {\n\tlog.Println(\"Received request for DeleteAllPlansHandler\")\n\n\tauth := Authenticate(w, r, true)\n\tif auth == nil {\n\t\treturn\n\t}\n\n\tvars := mux.Vars(r)\n\tprojectId := vars[\"projectId\"]\n\n\tlog.Println(\"projectId: \", projectId)","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/plans_crud.go#L240-L276","documentation":"DeletePlanHandler (plans_crud.go:258) fails after the DB row was successfully deleted but db.DeletePlanDir(orgId, planId) could not remove the plan's directory on disk. This error leaves the plan deleted in the database while its files remain, i.e. a partially completed delete. Typical causes are filesystem permission problems or a missing/locked directory.","triggerScenarios":"DELETE row succeeded, then DeletePlanDir errors because the plan directory does not exist, is owned by another user/UID (container permission drift), or a file inside is held open/locked by a running process.","commonSituations":"Server container running as non-root while plan dirs were created as root; volumes mounted with wrong permissions; plan dir already manually cleaned up; a running execution still holding files open.","solutions":["Inspect the logged driver error to see if it's ENOENT (dir already gone — safe to treat as success), EACCES/EPERM (fix ownership: chown -R the storage volume to the server user), or EBUSY (stop processes holding the dir).","Make DeletePlanDir idempotent: ignore os.IsNotExist so re-deleting an already-cleaned dir succeeds.","Verify storage volume mount permissions match the server process UID/GID.","Add a cleanup/reconciliation job that removes plan dirs with no matching DB row."],"exampleFix":"// before\nif err := os.RemoveAll(dir); err != nil {\n    return err\n}\n// after\nif err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {\n    return err\n}","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(planDir); os.IsNotExist(err) {\n    return nil // nothing to clean up\n}","typeGuard":"func isPermissionErr(err error) bool {\n    var pathErr *fs.PathError\n    return errors.As(err, &pathErr) && errors.Is(pathErr.Err, os.ErrPermission)\n}","tryCatchPattern":"if err := db.DeletePlanDir(orgId, planId); err != nil {\n    if os.IsNotExist(err) {\n        log.Println(\"plan dir already removed — continuing\")\n    } else {\n        log.Printf(\"Error deleting plan dir: %v\", err) // row already deleted; alert for cleanup job\n    }\n}","preventionTips":["Run the server with a UID that owns the storage volume; align chown/mount permissions.","Make DeletePlanDir idempotent by ignoring os.ErrNotExist.","Run a reconciliation job deleting orphaned plan directories.","Stop running executions before deleting their plan directories."],"tags":["filesystem","go","http-500","permissions"],"backgroundTag":"filesystem-permission-denied","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}