plandex-ai/plandex · error

error renaming plan: %v

Error message

error renaming plan: %v

What it means

RenamePlan updates the plans.name column, executing against either the supplied transaction (tx != nil) or the global Conn. This wrapper fires when the UPDATE fails. Because the function accepts an optional *sqlx.Tx, an error can also indicate the surrounding transaction was already aborted/rolled back, making every statement on it fail.

Source

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

	_, err := Conn.Exec("UPDATE branches SET status = $1, error = $2 WHERE plan_id = $3 AND name = $4", status, errStr, planId, branch)

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

	return nil
}

func RenamePlan(planId string, name string, tx *sqlx.Tx) error {
	var err error
	if tx == nil {
		_, err = Conn.Exec("UPDATE plans SET name = $1 WHERE id = $2", name, planId)
	} else {
		_, err = tx.Exec("UPDATE plans SET name = $1 WHERE id = $2", name, planId)
	}

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

	return nil
}

func IncActiveBranches(planId string, inc int, tx *sqlx.Tx) error {
	_, err := tx.Exec("UPDATE plans SET active_branches = active_branches + $1 WHERE id = $2", inc, planId)

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

	return nil
}

func IncNumNonDraftPlans(userId string, tx *sqlx.Tx) error {
	_, err := tx.Exec("UPDATE users SET num_non_draft_plans = num_non_draft_plans + 1 WHERE id = $1", userId)

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the inner error for unique-constraint violations and surface a 'name already taken' message to the user
  2. If a tx was passed, verify the transaction is still valid/committed — an aborted tx makes this UPDATE fail; roll back and retry the whole operation
  3. Validate the new name (length, allowed characters) against schema constraints before calling RenamePlan
  4. Confirm the plans row exists (GetPlan) before renaming to distinguish no-op from failure

Example fix

// before
_, err = tx.Exec("UPDATE plans SET name = $1 WHERE id = $2", name, planId)
if err != nil {
    return fmt.Errorf("error renaming plan: %v", err)
}
// after
_, err = tx.Exec("UPDATE plans SET name = $1 WHERE id = $2", name, planId)
if err != nil {
    var pgErr *pgconn.PgError
    if errors.As(err, &pgErr) && pgErr.Code == "23505" {
        return ErrPlanNameTaken
    }
    return fmt.Errorf("error renaming plan: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

var exists bool
if err := Conn.Get(&exists, "SELECT EXISTS(SELECT 1 FROM plans WHERE id = $1)", planId); err != nil || !exists {
    return fmt.Errorf("plan %s not found", planId)
}
if name == "" || len(name) > 255 {
    return fmt.Errorf("invalid plan name")
}

Type guard

func canRename(planId, name string) bool {
    var exists bool
    _ = Conn.Get(&exists, "SELECT EXISTS(SELECT 1 FROM plans WHERE id = $1)", planId)
    return exists && name != ""
}

Try / catch

if err := RenamePlan(planId, name, tx); err != nil {
    if strings.Contains(err.Error(), "duplicate key") || strings.Contains(err.Error(), "23505") {
        return fmt.Errorf("a plan named %q already exists", name)
    }
    tx.Rollback()
    return err
}

Prevention

When it happens

Trigger: Renaming a plan with a nil tx (uses Conn) or a live tx: DB connection failure, transaction already aborted, name violating a UNIQUE constraint on plans.name, or planId referencing a deleted plan (silent no-op unless a constraint fires).

Common situations: Renaming to a name that already exists when plans.name is unique; calling RenamePlan inside a transaction that a previous step already failed and rolled back; DB migration renamed the name column or added length limits exceeded by the new name.

Related errors


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