plandex-ai/plandex · error
error getting plan config: %v
Error message
error getting plan config: %v
What it means
GetPlanConfig wraps a failure of Conn.Get(&config, "SELECT plan_config FROM plans WHERE id = $1", planId) with this message. Because sqlx Get requires exactly one row, this error fires both for a nonexistent planId (sql.ErrNoRows) and for genuine query/scan failures.
Source
Thrown at app/server/db/plan_config_helpers.go:18
package db
import (
"fmt"
shared "plandex-shared"
"github.com/jmoiron/sqlx"
)
func GetPlanConfig(planId string) (*shared.PlanConfig, error) {
query := "SELECT plan_config FROM plans WHERE id = $1"
var config shared.PlanConfig
err := Conn.Get(&config, query, planId)
if err != nil {
return nil, fmt.Errorf("error getting plan config: %v", err)
}
return &config, nil
}
func StorePlanConfig(planId string, config *shared.PlanConfig) error {
query := `
UPDATE plans
SET plan_config = $1
WHERE id = $2
`
_, err := Conn.Exec(query, config, planId)
if err != nil {
return fmt.Errorf("error storing plan config: %v", err)
}
View on GitHub (pinned to e2d772072e)
Solutions
- Read the wrapped cause: 'sql: no rows in result set' means the planId doesn't exist — verify the plan exists before loading and return 404 to the client
- Check that the plan belongs to the caller's org/project if the id comes from user input
- If the cause is a scan/JSON error, inspect the plans.plan_config value for corruption and fix the data
- For connection errors, verify DATABASE_URL and Postgres health, then retry
Example fix
// before
config, err := GetPlanConfig(planId)
// after: distinguish not-found from real failures
config, err := GetPlanConfig(planId)
if err != nil {
if strings.Contains(err.Error(), "no rows in result set") {
http.Error(w, "plan not found", http.StatusNotFound)
return
}
http.Error(w, "internal error", http.StatusInternalServerError)
return
} Defensive patterns
Strategy: try-catch
Validate before calling
var exists bool
err := Conn.Get(&exists, "SELECT EXISTS(SELECT 1 FROM plans WHERE id=$1)", planId)
if err != nil || !exists {
// return 404 before calling GetPlanConfig
} Try / catch
config, err := GetPlanConfig(planId)
if err != nil {
if strings.Contains(err.Error(), "no rows in result set") {
http.Error(w, "plan not found", http.StatusNotFound)
return
}
log.Printf("GetPlanConfig: %v", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
} Prevention
- Treat 'no rows in result set' as not-found, not a crash
- Validate planId format (uuid) and ownership before lookup
- Never hard-delete plans; soft-delete so configs remain loadable
- Ensure plan_config JSONB values stay valid JSON (constrain writes)
When it happens
Trigger: LoadContexts/UpdateContexts/GetPlanConfigHandler pass a planId that doesn't exist in plans (most common: sql: no rows in result set), the DB is unreachable, or plan_config cannot be scanned into shared.PlanConfig (malformed JSON in the column).
Common situations: Client references a deleted plan; stale plan id cached in the UI; plan row deleted mid-session; database migrated so plan_config column type changed; corrupted JSONB value.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- error getting default plan config: %v
- error listing org roles: %v
- error storing plan config: %v
- error getting default plan config: %v
- error adding plan context tokens: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/47651a26226def07.
Report an issue: GitHub.