plandex-ai/plandex · error
error storing description: %v
Error message
error storing description: %v
What it means
Raised when db.StoreDescription(desc) fails inside the per-description goroutines that persist updated build flags for each unbuilt description. Errors are collected on descErrCh and the first one aborts the repo operation with 'error storing description: %v'. It means one of the ConvoMessageDescription updates (e.g. marking DidBuild=true, clearing BuildPathsInvalidated) could not be saved.
Source
Thrown at app/server/model/plan/build_finish.go:112
ConvoMessageDescriptions: planDescs,
})
if err != nil {
log.Printf("Error getting current plan state: %v\n", err)
return fmt.Errorf("error getting current plan state: %v", err)
}
descErrCh := make(chan error, len(unbuiltDescs))
for _, desc := range unbuiltDescs {
if len(desc.Operations) > 0 {
desc.DidBuild = true
desc.BuildPathsInvalidated = map[string]bool{}
}
go func(desc *db.ConvoMessageDescription) {
err := db.StoreDescription(desc)
if err != nil {
descErrCh <- fmt.Errorf("error storing description: %v", err)
return
}
descErrCh <- nil
}(desc)
}
for range unbuiltDescs {
err = <-descErrCh
if err != nil {
log.Printf("Error storing description: %v\n", err)
return err
}
}
err = repo.GitAddAndCommit(branch, currentPlan.PendingChangesSummaryForBuild())
if err != nil {View on GitHub (pinned to e2d772072e)
Solutions
- Inspect the wrapped err to identify the specific DB failure (connection vs constraint)
- Retry the build finish; parallel store calls often succeed on a second attempt after a transient outage
- Check for concurrent processes deleting or mutating description rows; serialize plan operations
- Verify description rows exist and satisfy constraints (no duplicates from forks/imports)
- Under sustained load, reduce concurrency or increase DB pool size
Example fix
// before: fire-and-forget goroutines can race with plan deletion
// go func(desc *db.ConvoMessageDescription) { descErrCh <- ... }(desc)
// after: check desc still valid before storing
if desc != nil && desc.Id != "" {
go func(desc *db.ConvoMessageDescription) {
descErrCh <- db.StoreDescription(desc)
}(desc)
} Defensive patterns
Strategy: retry
Validate before calling
if desc == nil || desc.Id == "" {
return fmt.Errorf("skipping invalid description before StoreDescription")
} Type guard
func isStorableDescription(d *db.ConvoMessageDescription) bool {
return d != nil && d.Id != "" && d.OrgId != "" && d.PlanId != ""
} Try / catch
err := db.StoreDescription(desc)
if err != nil {
descErrCh <- fmt.Errorf("error storing description: %w", err)
return
}
// caller: retry the whole StoreDescription batch on transient DB errors Prevention
- Add bounded retries with backoff to StoreDescription for transient errors
- Avoid deleting plan descriptions concurrently with an active build
- Size the DB connection pool for the fan-out of parallel store goroutines
- Use %w so errors.Is can distinguish constraint vs connection failures
When it happens
Trigger: After a build, for each unbuilt desc a goroutine calls db.StoreDescription(desc); the DB write fails (connection error, constraint violation, record deleted concurrently, context/transaction aborted) and the error is pushed to descErrCh and returned from the repo callback.
Common situations: DB connection pool exhaustion under many concurrent description stores; description row deleted by another process mid-build; unique/constraint violations from duplicate descriptions after a plan fork; Postgres restart during build finish.
Related errors
- error storing convo message description: %v
- panic in SyncPlanTokens: %v %s
- error getting contexts or convo: %v
- failed to get DB lock: %w
- error getting current plan state: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/51ce0f462e2ceb59.
Report an issue: GitHub.