plandex-ai/plandex · error

error reading convo file: %v

Error message

error reading convo file: %v

What it means

A worker goroutine failed to read one of the per-message JSON files in the plan convo directory via os.ReadFile. The error is sent to errCh and surfaced by GetPlanConvo wrapped as 'error reading convo files'. Unlike a missing whole directory, each individual file is expected to exist once ReadDir listed it, so this usually signals a race (file deleted mid-read) or permission problem.

Source

Thrown at app/server/db/convo_helpers.go:50

		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)
				return
			}

			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 {

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check whether another process/script is deleting files from the convo dir concurrently and stop it or serialize access
  2. Verify file ownership/permissions in the convo dir (ls -l) and fix with chown/chmod
  3. Remove non-regular entries (symlinks, temp files) from the convo dir
  4. Ensure only one Plandex server instance uses the same data directory
  5. Retry the request — if transient (deleted mid-read), it may succeed after cleanup settles

Example fix

// skip entries that are not regular files
// before
for _, file := range files {
    go func(file os.DirEntry) {
// after
for _, file := range files {
    if !file.Type().IsRegular() {
        continue
    }
    go func(file os.DirEntry) {
Defensive patterns

Strategy: retry

Validate before calling

for _, f := range mustListDir(convoDir) {
    if _, err := os.Stat(filepath.Join(convoDir, f.Name())); err != nil {
        log.Printf("convo file vanished between list and read: %s", f.Name())
    }
}

Type guard

func isRetryableReadErr(err error) bool {
    return errors.Is(err, os.ErrPermission) || errors.Is(err, syscall.EIO) || strings.Contains(err.Error(), "no such file")
}

Try / catch

var convo []*db.ConvoMessage
var err error
for i := 0; i < 3; i++ {
    convo, err = db.GetPlanConvo(orgId, planId)
    if err == nil || !isRetryableReadErr(err) {
        break
    }
    time.Sleep(100 * time.Millisecond << i)
}

Prevention

When it happens

Trigger: os.ReadFile(filepath.Join(convoDir, file.Name())) returns an error: the file was deleted between ReadDir and ReadFile (concurrent cleanup, plan deletion), permissions deny read, the entry is a broken symlink or special file, or name contains characters that break path resolution.

Common situations: Two server processes sharing one data dir where one deletes the plan; a cron/cleanup script purging old convo files while a user is viewing the convo; unreadable files after a restore with wrong ownership; non-regular files (sockets/temp files) accidentally placed in the convo dir.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/d318d346061f2c30. Report an issue: GitHub.