plandex-ai/plandex · error

error storing plan config: %v

Error message

error storing plan config: %v

What it means

StorePlanConfig wraps a failure of `UPDATE plans SET plan_config = $1 WHERE id = $2` with this message. Note the UPDATE silently affects zero rows when planId doesn't exist — this wrap only fires on actual Exec errors: connection failure, driver serialization error for the config, or schema problems.

Source

Thrown at app/server/db/plan_config_helpers.go:34

	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)
	}

	return nil
}

func GetDefaultPlanConfig(userId string) (*shared.PlanConfig, error) {
	query := "SELECT default_plan_config FROM users WHERE id = $1"

	var config shared.PlanConfig
	err := Conn.Get(&config, query, userId)

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

	return &config, nil
}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Inspect the wrapped cause for connection vs encoding errors; for connection errors check Postgres health and retry
  2. Confirm migrations have created plans.plan_config with the expected type (JSONB/text)
  3. Validate the incoming PlanConfig payload (JSON-serializable, size limits) before storing
  4. After a successful Exec, also check RowsAffected if you need to detect a nonexistent planId, since zero rows is not an error here

Example fix

// before
err := StorePlanConfig(planId, config)
// after: detect zero-row updates too
res, err := Conn.Exec("UPDATE plans SET plan_config = $1 WHERE id = $2", config, planId)
if err != nil { return err }
n, _ := res.RowsAffected()
if n == 0 { return fmt.Errorf("plan %s not found", planId) }
Defensive patterns

Strategy: validation

Validate before calling

// validate payload and target row before the UPDATE
b, err := json.Marshal(config)
if err != nil {
    return fmt.Errorf("invalid plan config: %w", err)
}
var exists bool
Conn.Get(&exists, "SELECT EXISTS(SELECT 1 FROM plans WHERE id=$1)", planId)

Try / catch

err := StorePlanConfig(planId, config)
if err != nil {
    log.Printf("StorePlanConfig(%s): %v", planId, err) // inspect cause
    http.Error(w, "failed to save config", http.StatusInternalServerError)
    return
}

Prevention

When it happens

Trigger: UpdatePlanConfigHandler calls StorePlanConfig and Conn.Exec fails: DB unreachable, plans table/column missing (migration drift), or the *shared.PlanConfig value cannot be encoded by the driver into plan_config.

Common situations: Postgres restart or connection-pool exhaustion during a config save; running against a database without the plan_config column; a PlanConfig containing unsupported field types the lib/pq encoder rejects; concurrent migration changing the schema.

Related errors


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