plandex-ai/plandex · error

error reading descriptions dir: %v

Error message

error reading descriptions dir: %v

What it means

GetConvoMessageDescriptions reads a plan's descriptions directory with os.ReadDir. A missing directory is treated as empty (os.IsNotExist short-circuit), but any other ReadDir failure (permissions, not a directory, I/O error) is wrapped as 'error reading descriptions dir: %v' and returned, failing the whole descriptions load.

Source

Thrown at app/server/db/result_helpers.go:320

	}

	planState.CurrentPlanFiles = currentPlanFiles

	return planState, nil
}

func GetConvoMessageDescriptions(orgId, planId string) ([]*ConvoMessageDescription, error) {
	var descriptions []*ConvoMessageDescription
	descriptionsDir := getPlanDescriptionsDir(orgId, planId)
	files, err := os.ReadDir(descriptionsDir)

	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)

View on GitHub (pinned to e2d772072e)

Solutions

  1. Read the wrapped inner error (errno) to identify permission vs not-a-directory vs I/O
  2. Check permissions/ownership of the descriptions directory for the server process user
  3. Confirm getPlanDescriptionsDir(orgId, planId) resolves to the correct data-root path
  4. If the path is a file or symlink target is broken, fix or remove it and recreate the directory
  5. If the plan has no descriptions and the dir is genuinely absent, no fix is needed — the library returns an empty list

Example fix

// before
files, err := os.ReadDir(descriptionsDir)
if err != nil {
    return nil, fmt.Errorf("error reading descriptions dir: %v", err)
}
// after
files, err := os.ReadDir(descriptionsDir)
if err != nil {
    if os.IsNotExist(err) || os.IsPermission(err) {
        return descriptions, nil // treat unreadable/missing dir as no descriptions
    }
    return nil, fmt.Errorf("error reading descriptions dir: %v", err)
}
Defensive patterns

Strategy: validation

Validate before calling

func descriptionsDirReadable(orgId, planId string) error {
    dir := getPlanDescriptionsDir(orgId, planId)
    info, err := os.Stat(dir)
    if os.IsNotExist(err) {
        return nil // treated as empty by the library
    }
    if err != nil {
        return err
    }
    if !info.IsDir() {
        return fmt.Errorf("%s is not a directory", dir)
    }
    f, err := os.Open(dir)
    if err != nil {
        return err
    }
    return f.Close()
}

Type guard

func isDescriptionsDirError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "error reading descriptions dir")
}

Try / catch

descriptions, err := GetConvoMessageDescriptions(orgId, planId)
if err != nil {
    if isDescriptionsDirError(err) {
        log.Printf("descriptions dir unreadable, treating as empty: %v", err)
        descriptions = nil
    } else {
        return nil, err
    }
}

Prevention

When it happens

Trigger: Calling GetConvoMessageDescriptions (directly or via GetCurrentPlanState / ClearContext / PendingBuildsByPath / invalidateConflictedResults) when descriptionsDir exists but cannot be listed: wrong permissions, path is a file not a directory, or an I/O error during listing.

Common situations: Server run under a different user than the one that created plan data (permission denied); getPlanDescriptionsDir path misconfigured after a data-root move so it points at a regular file; NFS/overlay filesystem returning EIO; symlink loops.

Related errors


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