plandex-ai/plandex · error

error reading applies dir: %v

Error message

error reading applies dir: %v

What it means

GetPlanApplies reads the plan's applies directory with os.ReadDir. Missing directories (os.IsNotExist) are treated as "no applies" and return nil, nil; any OTHER read error is wrapped as this error. It means the directory exists (or otherwise failed) but could not be listed — typically permissions or I/O problems.

Source

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

	}

	if !foundReplacement {
		return fmt.Errorf("replacement not found: %s", replacementId)
	}

	return nil
}

func GetPlanApplies(orgId, planId string) ([]*PlanApply, error) {
	appliesDir := getPlanAppliesDir(orgId, planId)
	files, err := os.ReadDir(appliesDir)

	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}

		return nil, fmt.Errorf("error reading applies dir: %v", err)
	}

	planApplies := []*PlanApply{}
	var mu sync.Mutex

	errCh := make(chan error, len(files))

	for _, file := range files {
		go func(file os.DirEntry) {
			defer func() {
				if r := recover(); r != nil {
					log.Printf("panic in GetPlanApplies: %v\n%s", r, debug.Stack())
					errCh <- fmt.Errorf("panic in GetPlanApplies: %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(appliesDir, file.Name()))

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check permissions on <planDir>/applies and its parents (server process user needs read+execute).
  2. Verify the path is a directory, not a file: 'ls -la' / 'stat' on the applies path.
  3. Check disk health / dmesg for I/O errors if failures are intermittent.
  4. Fix ownership to match the server process user.

Example fix

// before
applies, err := db.GetPlanApplies(orgId, planId)
// after (pre-check on host)
// stat <data>/orgs/<orgId>/plans/<planId>/applies && ensure it is a dir with correct perms
applies, err := db.GetPlanApplies(orgId, planId)
Defensive patterns

Strategy: validation

Validate before calling

appliesPath := filepath.Join(getPlanDir(orgId, planId), "applies")
if info, err := os.Stat(appliesPath); err == nil && !info.IsDir() {
    return fmt.Errorf("%s is not a directory", appliesPath)
}
applies, err := GetPlanApplies(orgId, planId)

Try / catch

applies, err := GetPlanApplies(orgId, planId)
if err != nil {
    if strings.Contains(err.Error(), "error reading applies dir") {
        return nil, fmt.Errorf("cannot list applies dir (check permissions/path): %w", err)
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling GetPlanApplies when the applies directory exists but ReadDir fails for a non-not-exist reason: permission denied, path is a file not a directory, or an I/O/disk error.

Common situations: Manual intervention replaced the applies directory with a regular file; chmod/chown changes on the data dir; running the server under a different user than the one that created the data.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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