plandex-ai/plandex · error
error reading convo files: %v
Error message
error reading convo files: %v
What it means
GetPlanConvo collects len(files) results from buffered errCh/convoCh channels; the first error received on errCh aborts the whole read and is wrapped as 'error reading convo files'. It is the aggregator for all per-goroutine failures — read errors (342), unmarshal errors (343), or recovered panics (341) — so the real cause is the inner wrapped error.
Source
Thrown at app/server/db/convo_helpers.go:70
}
var convoMessage ConvoMessage
err = json.Unmarshal(bytes, &convoMessage)
if err != nil {
errCh <- fmt.Errorf("error unmarshalling convo file: %v", err)
return
}
convoCh <- &convoMessage
}(file)
}
for i := 0; i < len(files); i++ {
select {
case err := <-errCh:
return nil, fmt.Errorf("error reading convo files: %v", err)
case convoMessage := <-convoCh:
convo = append(convo, convoMessage)
}
}
sort.Slice(convo, func(i, j int) bool {
return convo[i].CreatedAt.Before(convo[j].CreatedAt)
})
return convo, nil
}
func GetConvoMessage(orgId, planId, messageId string) (*ConvoMessage, error) {
convoDir := getPlanConversationDir(orgId, planId)
filePath := filepath.Join(convoDir, messageId+".json")
bytes, err := os.ReadFile(filePath)View on GitHub (pinned to e2d772072e)
Solutions
- Read the inner error embedded in this message to identify the failing file and root cause (missing, corrupt, or panic)
- Delete or repair the specific bad file in the convo directory
- Fix permissions/ownership on the convo directory contents
- Restore the plan's convo directory from backup if multiple files are damaged
- Stop concurrent writers/deleters of the same data dir (single server instance per data dir)
Defensive patterns
Strategy: try-catch
Validate before calling
files, err := os.ReadDir(convoDir)
if err == nil {
for _, f := range files {
if data, err := os.ReadFile(filepath.Join(convoDir, f.Name())); err == nil {
var m db.ConvoMessage
if err := json.Unmarshal(data, &m); err != nil {
log.Printf("precheck: bad convo file %s: %v", f.Name(), err)
}
}
}
} Type guard
func isConvoAggregateErr(err error) bool {
return strings.HasPrefix(err.Error(), "error reading convo files:")
} Try / catch
convo, err := db.GetPlanConvo(orgId, planId)
if err != nil {
if isConvoAggregateErr(err) {
inner := strings.TrimPrefix(err.Error(), "error reading convo files: ")
log.Printf("plan convo degraded, inner cause: %s", inner)
}
return err
} Prevention
- Parse the inner error to find the single bad file before touching storage
- Quarantine rather than delete corrupt files
- Ensure single-writer access to the plan data dir
- Test convo reads after every crash/restore event
When it happens
Trigger: Any of the per-file goroutines fails: a convo file deleted/corrupt/unreadable, a panic in a worker, or (because select returns on the first error) any single bad file poisons the entire convo read even when other files are fine.
Common situations: One corrupt message JSON making the whole plan history unviewable after a crash; a race with plan deletion during concurrent access; wrong permissions on a single file after restore; mixed old/new schema files after an upgrade.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- failed to read the file %s: %v
- error unmarshalling settings-v2.json: %v
- error getting plan current branch: %v
- error deleting draft plan dir: %v
- panic in DeleteOwnerPlans: %v %s
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/71db3f8d5fd5f6c2.
Report an issue: GitHub.