plandex-ai/plandex · warning
Not found
Error message
Not found
What it means
DeletePlanHandler (plans_crud.go:250) returns 404 'Not found' when the DELETE statement affected zero rows, meaning no plan row with the given planId existed in the plans table. Note: authorization already passed (the plan appeared to exist for this user), so this usually indicates a race or an ID mismatch rather than a normal missing-plan case.
Source
Thrown at app/server/handlers/plans_crud.go:250
res, err := db.Conn.Exec("DELETE FROM plans WHERE id = $1", planId)
if err != nil {
log.Printf("Error deleting plan: %v\n", err)
http.Error(w, "Error deleting plan: "+err.Error(), http.StatusInternalServerError)
return
}
rowsAffected, err := res.RowsAffected()
if err != nil {
log.Printf("Error getting rows affected: %v\n", err)
http.Error(w, "Error getting rows affected: "+err.Error(), http.StatusInternalServerError)
return
}
if rowsAffected == 0 {
log.Println("Plan not found")
http.Error(w, "Not found", http.StatusNotFound)
return
}
err = db.DeletePlanDir(auth.OrgId, planId)
if err != nil {
log.Printf("Error deleting plan dir: %v\n", err)
http.Error(w, "Error deleting plan dir: "+err.Error(), http.StatusInternalServerError)
return
}
log.Println("Successfully deleted plan", planId)
}
func DeleteAllPlansHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Received request for DeleteAllPlansHandler")
auth := Authenticate(w, r, true)View on GitHub (pinned to e2d772072e)
Solutions
- Treat 404 as success on retry/delete flows — if the goal was deletion, the plan is already gone; refresh the plan list.
- Verify the planId in the request URL matches an existing row (SELECT id FROM plans WHERE id = ...).
- Confirm the server is connected to the expected database/environment (check DATABASE_URL / db config).
- Add a UNIQUE guard or soft-delete flag if concurrent deletes of the same plan are common in your app.
Example fix
// client-side before await api.deletePlan(planId); // 404 throws // after const res = await api.deletePlan(planId); if (res.status === 404) return; // already deleted — success for idempotent delete
Defensive patterns
Strategy: fallback
Validate before calling
const exists = await fetch(`/api/plans/${planId}`).then(r => r.status !== 404);
if (!exists) return; // nothing to delete Type guard
func planExists(ctx context.Context, db *sql.DB, planId string) (bool, error) {
var one int
err := db.QueryRowContext(ctx, "SELECT 1 FROM plans WHERE id = $1", planId).Scan(&one)
if errors.Is(err, sql.ErrNoRows) { return false, nil }
return err == nil, err
} Try / catch
const res = await fetch(`/api/plans/${planId}`, { method: 'DELETE' });
if (res.status === 404) {
// already deleted — treat as success in idempotent delete flows
} else if (!res.ok) {
throw new Error(await res.text());
} Prevention
- Design delete endpoints/clients to be idempotent — 404 on delete means success.
- Refresh the plan list after deletion to avoid stale UI entries.
- Guard against double-submit (disable the button while the request is in flight).
- Confirm you are pointed at the correct environment/database.
When it happens
Trigger: DELETE FROM plans WHERE id = $1 matches 0 rows: the plan was deleted by another user/request between authorizePlanDelete and the DELETE, the planId path variable is wrong or points to an already-purged plan, or the row lives in a different database/environment than expected.
Common situations: Double-clicking a delete button so the second request 404s; stale UI listing a plan deleted elsewhere; pointing a dev client at a prod (or empty) database; IDs from a different environment after a DB reset.
Related errors
- error invalidating conflicted results: %v
- error adding plan context tokens: %v
- error reading convo file: %v
- org not found
- error adding org member: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/f1edeaaed521adae.
Report an issue: GitHub.