plandex-ai/plandex · error
error reading convo dir: %v
Error message
error reading convo dir: %v
What it means
GetPlanConvo lists the plan's conversation directory with os.ReadDir and builds the message list from its JSON files. A missing directory is treated as an empty convo (nil error), so this error means ReadDir failed for a reason other than non-existence — permission, I/O, or the path is not a directory. It wraps the underlying *PathError so the OS message is preserved.
Source
Thrown at app/server/db/convo_helpers.go:32
shared "plandex-shared"
"github.com/fatih/color"
"github.com/google/uuid"
"github.com/sashabaranov/go-openai"
)
func GetPlanConvo(orgId, planId string) ([]*ConvoMessage, error) {
var convo []*ConvoMessage
convoDir := getPlanConversationDir(orgId, planId)
files, err := os.ReadDir(convoDir)
if err != nil {
if os.IsNotExist(err) {
return convo, nil
}
return nil, fmt.Errorf("error reading convo dir: %v", err)
}
errCh := make(chan error, len(files))
convoCh := make(chan *ConvoMessage, len(files))
for _, file := range files {
go func(file os.DirEntry) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in GetPlanConvo: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("panic in GetPlanConvo: %v\n%s", r, debug.Stack())
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
bytes, err := os.ReadFile(filepath.Join(convoDir, file.Name()))
if err != nil {
errCh <- fmt.Errorf("error reading convo file: %v", err)View on GitHub (pinned to e2d772072e)
Solutions
- Check permissions/ownership on the convo directory path returned in the wrapped error and chown/chmod to the server user
- Stat the path to see if it is a file instead of a directory; remove or restore it to a directory
- Restore the plans data directory from backup if it was deleted or corrupted
- Verify the server's data-dir configuration env var points at the intended storage root
- Check disk/volume health (dmesg, mount status) if the underlying error is an I/O error
Example fix
// operator fix: data dir owned by root after image change // before ls -l /plandex-data/plans/org123/plan456/convo # owned by root // after chown -R plandex:plandex /plandex-data/plans
Defensive patterns
Strategy: try-catch
Validate before calling
if info, err := os.Stat(convoDir); err != nil {
// treat as missing convo, but log non-NotExist errors early
} else if !info.IsDir() {
return fmt.Errorf("convo path %s is not a directory", convoDir)
} Type guard
func isPathErr(err error) (*os.PathError, bool) {
var pe *os.PathError
if errors.As(err, &pe) {
return pe, true
}
return nil, false
} Try / catch
convo, err := db.GetPlanConvo(orgId, planId)
if err != nil {
if strings.Contains(err.Error(), "permission denied") {
// alert ops: data dir ownership/permissions wrong
}
return fmt.Errorf("plan convo unavailable: %w", err)
} Prevention
- Run the server as a single dedicated user and own the entire data dir with it
- Never place files at paths the app expects to be directories
- Monitor data-dir volume health and mount state
- Do not run concurrent cleanup scripts against live plan storage
When it happens
Trigger: os.ReadDir(convoDir) fails with a non-ENOENT error: permissions revoked on the plans/org data directory, the convo path exists but is a regular file or symlink to nowhere, disk I/O error, or the directory was removed/renamed between the existence check paths by an external process (e.g. manual cleanup, backup job, NFS hiccup).
Common situations: Server runs as a different user than the one that created the plan data (data-dir owned by root after a container/image change); an operator deleted or moved plans data directory while the server was running; wrong PLANDELANDX data-dir env config pointing at a file; read-only volume mount after storage remount.
Related errors
- error creating directory: %v
- error reading settings-v2.json: %v
- failed to seek in temporary file: %w
- error reading convo file: %v
- error reading convo message: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/56521d9ff6de623e.
Report an issue: GitHub.