plandex-ai/plandex · error
error storing convo message description: %v
Error message
error storing convo message description: %v
What it means
ApplyPlan persists each ConvoMessageDescription via StoreDescription inside a goroutine. This error means StoreDescription returned a non-nil error; it is wrapped verbatim, so the database-layer cause (connection failure, constraint violation, timeout) is embedded in the message. The description is not applied and the goroutine reports the failure on errCh.
Source
Thrown at app/server/db/result_helpers.go:615
}(result)
}
for _, description := range convoMessageDescriptions {
go func(description *ConvoMessageDescription) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in ApplyPlan: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("panic in ApplyPlan: %v\n%s", r, debug.Stack())
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
description.AppliedAt = &now
err := StoreDescription(description)
if err != nil {
errCh <- fmt.Errorf("error storing convo message description: %v", err)
return
}
errCh <- nil
}(description)
}
if len(pendingNewFilesSet) > 0 {
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in ApplyPlan: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("panic in ApplyPlan: %v\n%s", r, debug.Stack())
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
loadReq := shared.LoadContextRequest{}
for path := range pendingNewFilesSet {View on GitHub (pinned to e2d772072e)
Solutions
- Inspect the wrapped error for the underlying DB cause (connection refused, constraint name, timeout)
- Verify database connectivity and that the app's DB credentials/migrations are current
- If it is a duplicate-key error, make StoreDescription idempotent (upsert) or skip already-applied descriptions
- Retry the failed description(s) — the buffer-sized errCh means other items still completed
- Log description.Id with the error to correlate failures for re-apply
Example fix
// before
err := StoreDescription(description)
if err != nil {
errCh <- fmt.Errorf("error storing convo message description: %v", err)
// after (idempotent store)
err := UpsertDescription(description) // ON CONFLICT (id) DO UPDATE
if err != nil {
errCh <- fmt.Errorf("error storing convo message description %s: %w", description.Id, err) Defensive patterns
Strategy: retry
Validate before calling
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("database unreachable: %w", err)
}
if description == nil || description.Id == "" {
return fmt.Errorf("invalid description")
} Try / catch
err := <-errCh
if err != nil && strings.Contains(err.Error(), "error storing convo message description") {
// parse wrapped DB error; retry transient failures with backoff
var netErr net.Error
if errors.As(err, &netErr) || errors.Is(err, sql.ErrConnDone) {
// re-run ApplyPlan or re-store the failed description
}
} Prevention
- Apply DB migrations before deploying; verify connection pool limits
- Make StoreDescription idempotent (upsert) to survive plan re-application
- Check errCh after ApplyPlan and re-apply failed descriptions
- Monitor DB health; treat connection errors as transient and retryable
When it happens
Trigger: StoreDescription(description) fails — e.g. the database is unreachable, the description violates a unique/FK constraint, the record is too large, a transaction times out, or a required field is empty and the store layer rejects it.
Common situations: DB connection pool exhausted or DB restarted mid-apply; re-applying the same plan causing duplicate-key violations; migration mismatch so the convo_message_descriptions table lacks an expected column; network partition between app and database.
Related errors
- error storing 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/7786b94331f30d18.
Report an issue: GitHub.