plandex-ai/plandex · error

error creating plan: %v

Error message

error creating plan: %v

What it means

This error wraps any failure from the INSERT into the 'plans' table inside CreatePlan (app/server/db/plan_helpers.go:59). The insert uses tx.QueryRow(...).Scan(...) with a RETURNING clause, so it fails when the INSERT itself errors or when scanning the returned id/created_at/updated_at fails. Because it runs inside a WithTx transaction, a failure here aborts the whole plan-creation transaction.

Source

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

			Name:       name,
			PlanConfig: planConfig,
		}

		err = tx.QueryRow(
			query,
			orgId,
			userId,
			projectId,
			name,
			planConfig,
		).Scan(
			&plan.Id,
			&plan.CreatedAt,
			&plan.UpdatedAt,
		)

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

		_, err = tx.Exec("INSERT INTO lockable_plan_ids (plan_id) VALUES ($1)", plan.Id)

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

		// the one place where we do this to skip the locking queue
		// ok to cheat this once since we're creating a new plan
		repo := getGitRepo(orgId, plan.Id)
		_, err = CreateBranch(repo, plan, nil, "main", tx)

		if err != nil {
			return fmt.Errorf("error creating main branch: %v", err)
		}

		log.Println("Created branch main")

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the wrapped %v detail in logs for the underlying pq/lib/pq error code and address it specifically (unique_violation, foreign_key_violation, etc.)
  2. Verify the database schema matches migrations (run any pending migrations; confirm plans has id, created_at, updated_at, plan_config columns)
  3. Confirm the org_id, projectId, and userId passed to CreatePlan exist and the DB connection is healthy
  4. If it's a transient connection error, retry CreatePlan; the transaction rolls back cleanly so a retry is safe

Example fix

// before (hard to diagnose)
return fmt.Errorf("error creating plan: %v", err)
// after (surface the DB error class)
var pqErr *pq.Error
if errors.As(err, &pqErr) {
    return fmt.Errorf("error creating plan: code=%s constraint=%s: %v", pqErr.Code, pqErr.Constraint, pqErr)
}
return fmt.Errorf("error creating plan: %w", err)
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: verify inputs and DB reachability before CreatePlan
if orgId == "" || projectId == "" || userId == "" || name == "" {
    return fmt.Errorf("create plan: orgId, projectId, userId and name are required")
}
if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("database unreachable: %w", err)
}

Type guard

func asPQError(err error) (*pq.Error, bool) {
    var pqErr *pq.Error
    if errors.As(err, &pqErr) {
        return pqErr, true
    }
    return nil, false
}

Try / catch

plan, err := db.CreatePlan(ctx, orgId, projectId, userId, name)
if err != nil {
    var pqErr *pq.Error
    if errors.As(err, &pqErr) && pqErr.Code == "23505" {
        // duplicate plan: surface a friendly message
        return nil, ErrPlanAlreadyExists
    }
    return nil, fmt.Errorf("create plan failed: %w", err)
}

Prevention

When it happens

Trigger: Calling CreatePlan when the plans insert violates a constraint (e.g. duplicate plan name if unique, invalid org_id/user_id/project_id foreign keys), when the database connection is down, when plan_config serialization is incompatible with the column type, or when Scan cannot decode the RETURNING columns (schema/type mismatch between the Plan struct and the table).

Common situations: Database migrations out of sync with the deployed code (missing or altered plans columns), Postgres connection pool exhaustion or transient connection drops, FK violations from a project or user that was deleted concurrently, or running against an older Postgres that rejects the plan_config format.

Related errors


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