plandex-ai/plandex · critical
panic in GetConvoMessageDescriptions: %v\n%s
Error message
panic in GetConvoMessageDescriptions: %v\n%s
What it means
GetConvoMessageDescriptions spawns one goroutine per description file, each guarded by a deferred recover(). If reading or parsing a file panics (nil deref, out-of-range, etc.), the recover converts the panic value plus debug.Stack() into 'panic in GetConvoMessageDescriptions: %v\n%s', sends it on errCh, and calls runtime.Goexit to stop the goroutine without double-sending.
Source
Thrown at app/server/db/result_helpers.go:331
if err != nil {
if os.IsNotExist(err) {
return descriptions, nil
}
return nil, fmt.Errorf("error reading descriptions dir: %v", err)
}
errCh := make(chan error, len(files))
descCh := make(chan *ConvoMessageDescription, len(files))
for _, file := range files {
go func(file os.DirEntry) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in GetConvoMessageDescriptions: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("panic in GetConvoMessageDescriptions: %v\n%s", r, debug.Stack())
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
path := filepath.Join(descriptionsDir, file.Name())
bytes, err := os.ReadFile(path)
if err != nil {
errCh <- fmt.Errorf("error reading description file %s: %v", file.Name(), err)
return
}
var description ConvoMessageDescription
err = json.Unmarshal(bytes, &description)
if err != nil {
log.Println("Error unmarshalling description file:", path)
log.Println("bytes:")View on GitHub (pinned to e2d772072e)
Solutions
- Extract the stack trace from the error message and locate the panicking line in the per-file goroutine
- Check the server log for the identical log.Printf trace for goroutine context
- Identify the specific description file involved and inspect/repair or remove it
- Ensure ConvoMessageDescription (and any custom UnmarshalJSON) handles nil/empty input without panicking
- Consider bounding per-file goroutine concurrency with a worker pool if memory pressure triggers panics
Example fix
// before
bytes, err := os.ReadFile(path)
if err != nil {
errCh <- fmt.Errorf("error reading description file %s: %v", file.Name(), err)
return
}
// after
if !file.Type().IsRegular() {
descCh <- nil // skip non-regular entries like sockets/dirs
return
}
bytes, err := os.ReadFile(path)
if err != nil {
errCh <- fmt.Errorf("error reading description file %s: %v", file.Name(), err)
return
} Defensive patterns
Strategy: try-catch
Validate before calling
// list and pre-screen entries before triggering per-file goroutines
entries, err := os.ReadDir(getPlanDescriptionsDir(orgId, planId))
if err == nil {
for _, e := range entries {
if !e.Type().IsRegular() {
log.Printf("skipping non-regular description entry: %s", e.Name())
}
}
} Type guard
func isDescParsePanicError(err error) bool {
return err != nil && strings.Contains(err.Error(), "panic in GetConvoMessageDescriptions")
} Try / catch
descriptions, err := GetConvoMessageDescriptions(orgId, planId)
if err != nil {
if isDescParsePanicError(err) {
log.Printf("per-file goroutine panic, stack embedded:\n%s", err)
return nil, err
}
return nil, err
} Prevention
- Keep only regular files inside descriptions directories (no subdirs/sockets)
- Ensure any custom UnmarshalJSON on ConvoMessageDescription cannot panic on malformed input
- Bound concurrency for very large plans to avoid memory-pressure panics
- Log and archive the embedded stack trace on every occurrence
When it happens
Trigger: A panic occurs inside a per-file goroutine (result_helpers.go:327-357) — e.g. during os.ReadFile, json.Unmarshal into ConvoMessageDescription, or while handling a file whose Name() or path is unexpected (special files, entries vanished mid-iteration).
Common situations: Race between ReadDir and file deletion (file removed between listing and ReadFile in exotic setups); extremely large or zero-length files causing unexpected library panics; custom UnmarshalJSON panicking on malformed data; memory pressure under very large plans with thousands of concurrent per-file goroutines.
Related errors
- panic in UpdateContexts: %v\n%s
- panic in GetPlanConvo: %v\n%s
- error reading convo files: %v
- panic in gitRemoveIndexLockFileIfExists: %v %s
- panic in SyncPlanTokens: %v %s
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/7c687029c21fbc1d.
Report an issue: GitHub.