plandex-ai/plandex · error

failed to read the file %s: %v

Error message

failed to read the file %s: %v

What it means

During the outdated check, each file-type context is read concurrently with os.ReadFile to hash/compare against stored state. If a file that passed the initial os.Stat existence check cannot be read (permission, race with deletion, I/O error), the error is collected into the errs list with the file path.

Source

Thrown at app/cli/lib/context_update.go:388

				defer wg.Done()
				sem <- struct{}{}
				defer func() { <-sem }()

				if _, err := os.Stat(ctx.FilePath); os.IsNotExist(err) {
					mu.Lock()
					defer mu.Unlock()

					deleteIds[ctx.Id] = true
					numFilesRemoved++
					tokenDiffsById[ctx.Id] = -ctx.NumTokens
					return
				}

				fileContent, err := os.ReadFile(ctx.FilePath)
				if err != nil {
					mu.Lock()
					defer mu.Unlock()
					errs = append(errs, fmt.Errorf("failed to read the file %s: %v", ctx.FilePath, err))
					return
				}
				fileContent = shared.NormalizeEOL(fileContent)

				fileInfo, err := os.Stat(ctx.FilePath)
				if err != nil {
					mu.Lock()
					defer mu.Unlock()
					errs = append(errs, fmt.Errorf("failed to get file info for %s: %v", ctx.FilePath, err))
					return
				}
				size := fileInfo.Size()

				// Individual skip checks
				if size > shared.MaxContextBodySize {
					mu.Lock()
					defer mu.Unlock()

View on GitHub (pinned to e2d772072e)

Solutions

  1. Fix permissions on the reported file (chmod/chown) or run as a user with read access
  2. If the file no longer exists, remove it from context (plandex context rm) and re-add
  3. Re-run the command — race-related failures usually clear
  4. Check the filesystem/mount health if errors persist across many files

Example fix

// before: ignoring collected errs
outdated, err := lib.CheckOutdatedContext(nil, paths)
// after: ensure all context files are readable before checking
for _, c := range contexts {
    if c.ContextType == shared.ContextFileType {
        if f, err := os.Open(c.FilePath); err != nil {
            log.Fatalf("cannot read context file %s: %v", c.FilePath, err)
        } else { f.Close() }
    }
}
outdated, err := lib.CheckOutdatedContext(contexts, paths)
Defensive patterns

Strategy: validation

Validate before calling

for _, c := range contexts {
    if c.ContextType == shared.ContextFileType {
        f, err := os.Open(c.FilePath)
        if err != nil {
            return fmt.Errorf("pre-flight: cannot open %s: %w", c.FilePath, err)
        }
        f.Close()
    }
}

Try / catch

outdated, err := lib.CheckOutdatedContext(contexts, paths)
if err != nil {
    if strings.Contains(err.Error(), "failed to read the file ") {
        // extract path, drop it from context or fix perms, then retry
        return fmt.Errorf("fix unreadable context file, then re-run: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: os.ReadFile(ctx.FilePath) fails after os.Stat succeeded: permission denied on the file, file deleted between stat and read (TOCTOU race), read-only filesystem, or file is a special device/pipe that cannot be read normally.

Common situations: CI running as a different user than the one who added context; files on a network mount that dropped; editor/tooling swapping files (atomic rename) mid-check; chmod changes after adding files to context.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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