plandex-ai/plandex · error

error deleting plan dir: %v

Error message

error deleting plan dir: %v

What it means

After DeleteOwnerPlans fans out deletion goroutines, it collects len(ids) results from errCh. The first non-nil error — including DeletePlanDir filesystem failures like permission denied or ENOENT — is wrapped with this message and returned, aborting the remaining error collection loop.

Source

Thrown at app/server/db/plan_helpers.go:437

	errCh := make(chan error, len(ids))
	for _, planId := range ids {
		go func(planId string) {
			defer func() {
				if r := recover(); r != nil {
					log.Printf("panic in DeleteOwnerPlans: %v\n%s", r, debug.Stack())
					errCh <- fmt.Errorf("panic in DeleteOwnerPlans: %v\n%s", r, debug.Stack())
					runtime.Goexit() // don't allow outer function to continue and double-send to channel
				}
			}()
			errCh <- DeletePlanDir(orgId, planId)
		}(planId)
	}

	for i := 0; i < len(ids); i++ {
		err := <-errCh
		if err != nil {
			return fmt.Errorf("error deleting plan dir: %v", err)
		}
	}

	if len(ids) > 0 {
		log.Println("Deleted", len(ids), "plans")
	}

	return nil
}

func ValidatePlanAccess(planId, userId, orgId string) (*Plan, error) {
	// get plan
	plan, err := GetPlan(planId)

	if err != nil {
		return nil, fmt.Errorf("error getting plan: %v", err)
	}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Read the wrapped inner error to identify the failing planId and the exact OS error
  2. Check filesystem permissions and ownership of the plan directories
  3. Make deletion idempotent: ignore os.ErrNotExist when removing plan dirs
  4. Re-run DeleteAllPlansHandler after cleanup; note the loop returns on first error so remaining plans may need a retry

Example fix

// before
if err := os.RemoveAll(dir); err != nil {
    return err
}
// after
if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {
    return err
}
return nil
Defensive patterns

Strategy: try-catch

Validate before calling

for _, dir := range planDirs(ids) {
    if _, err := os.Stat(dir); err != nil && !os.IsNotExist(err) {
        return fmt.Errorf("plan dir not accessible: %w", err)
    }
}

Try / catch

if err := DeleteOwnerPlans(orgId, ids); err != nil {
    var pathErr *os.PathError
    if errors.As(err, &pathErr) && os.IsNotExist(pathErr) {
        // already deleted; treat as success
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: DeletePlanDir (invoked via DeleteOwnerPlans from DeleteAllPlansHandler) fails for any plan id — e.g. os.Remove errors because the plan directory is missing, permissions are wrong, or the directory is non-empty on some platforms.

Common situations: Plan directories already manually deleted from disk, running the server as a user without write access to the plans data directory, or leftover state after a partially failed prior deletion.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/799dfe34bfcc0d4d. Report an issue: GitHub.