abhigyanpatwari/GitNexus · error

Failed to read file

Error message

Failed to read file

What it means

A 500 response from the file-read endpoint (GET /api/file). If the requested file cannot be read and the error is not a storage-requirement error (which maps to 410), the handler returns err.message or the fallback 'Failed to read file'.

Solutions

  1. Verify the requested path exists in the repo working tree and is relative to the repo root
  2. Re-run `gitnexus analyze` to refresh the index if the working tree changed since indexing
  3. Check file permissions on the file for the server process user
  4. If the response is 410 (not this 500), the index uses non-full content retention — re-analyze or use local file access

Example fix

// before: requesting a file that moved
fetch('/api/file?path=src/old-name.ts') // 500 Failed to read file

// after: refresh index / use the current path
cd my-repo && gitnexus analyze
fetch('/api/file?path=src/new-name.ts') // 200
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from 'fs';
const p = path.resolve(repoRoot, relPath);
if (!p.startsWith(path.resolve(repoRoot) + path.sep) || !existsSync(p)) throw new Error('bad file path');

Try / catch

const res = await fetch(`/api/file?path=${encodeURIComponent(p)}`);
if (res.status === 500) {
  const { error } = await res.json();
  console.warn('File read failed:', error); // re-index or fix the path
}

Prevention

When it happens

Trigger: GET /api/file?path=... where handleFileRequest throws: the path resolves outside the repo (traversal guard), the file was deleted after indexing, or a filesystem error occurs during readFile.

Common situations: Common when the working tree changed since indexing (file renamed/removed), the requested path uses wrong relative form, or the repo checkout moved on disk.

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 abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15). Data as JSON: /api/errors/7adafa330e840c99. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/server/api.ts:1578

    } catch (err: any) {
      if (sendStorageRequirementHttp(err, res)) return;
      res.status(500).json(httpErrorBody(err, 'Search failed'));
    }
  });

  // Read file — with path traversal guard
  // Rate-limited (CodeQL js/missing-rate-limiting): per-request fs.readFile.
  app.get('/api/file', createRouteLimiter(), async (req, res) => {
    try {
      const entry = await resolveRepo(requestedRepo(req));
      if (!entry) {
        res.status(404).json({ error: 'Repository not found' });
        return;
      }
      await handleFileRequest(req, res, entry.path, await getSourceAvailability(entry));
    } catch (err: any) {
      if (sendStorageRequirementHttp(err, res)) return;
      res.status(500).json({ error: err.message || 'Failed to read file' });
    }
  });

  // Grep — regex search across file contents in the indexed repo
  // Uses filesystem-based search for memory efficiency (never loads all files into memory)
  // Rate-limited (CodeQL js/missing-rate-limiting): scans every file in
  // the indexed repo per request — heaviest I/O endpoint. Same default 60
  // rpm/IP for now; consider tightening if real-world load shows abuse.
  app.get('/api/grep', createRouteLimiter(), async (req, res) => {
    try {
      const entry = await resolveRepo(requestedRepo(req));
      if (!entry) {
        res.status(404).json({ error: 'Repository not found' });
        return;
      }
      const sourceAvailability = await getSourceAvailability(entry);
      if (!sourceAvailability.available) {
        sendSourceUnavailable(res, sourceAvailability);

View on GitHub (pinned to ac9a4e9abd)