Crosstalk-Solutions/project-nomad · error · Error

Failed to read file stream: ${filename}

Error message

Failed to read file stream: ${filename}

What it means

Thrown when the docs file exists (stats found) but getFile(fullPath, 'stream') returns a falsy stream — the storage layer could not open the file for reading despite it being stat-able. This indicates an I/O or permission-level failure rather than a missing file.

Source

Thrown at admin/app/services/docs_service.ts:87

      }

      const filename = _filename.endsWith('.md') ? _filename : `${_filename}.md`

      // Prevent path traversal — resolved path must stay within the docs directory
      const basePath = path.resolve(this.docsPath)
      const fullPath = path.resolve(path.join(this.docsPath, filename))
      if (!fullPath.startsWith(basePath + path.sep)) {
        throw new Error('Invalid document slug')
      }

      const fileExists = await getFileStatsIfExists(fullPath)
      if (!fileExists) {
        throw new Error(`File not found: ${filename}`)
      }

      const fileStream = await getFile(fullPath, 'stream')
      if (!fileStream) {
        throw new Error(`Failed to read file stream: ${filename}`)
      }
      const content = await streamToString(fileStream)
      return this.parse(content)
    } catch (error) {
      throw new InternalServerErrorException(`Error parsing file: ${(error as Error).message}`)
    }
  }

  private static readonly TITLE_OVERRIDES: Record<string, string> = {
    'faq': 'FAQ',
    'community-add-ons': 'Community Add-Ons',
  }

  private prettify(filename: string) {
    const slug = filename.replace(/\.md$/, '')
    if (DocsService.TITLE_OVERRIDES[slug]) {
      return DocsService.TITLE_OVERRIDES[slug]
    }

View on GitHub (pinned to 0bd1c6f4f9)

Solutions

  1. Check process read permissions: run as the app user, `head <docsPath>/<slug>.md`
  2. Fix ownership/mode: chown/chmod the docs directory to the app's uid/gid
  3. If getFile wraps a storage SDK, inspect its logs for the underlying open failure
  4. Handle deletion races by re-checking existence and returning a not-found response

Example fix

// before
// docs dir owned by root; app user cannot open files
const doc = await docs.parseFile('faq')

// after (ops fix)
// chown -R appuser:appuser /path/to/docs && chmod -R u+rwX /path/to/docs
const doc = await docs.parseFile('faq')
Defensive patterns

Strategy: try-catch

Validate before calling

import { access } from 'node:fs/promises'
await access(path.join(docsPath, `${slug}.md`), fsConstants.R_OK)
// readable — stream failure is then unlikely

Try / catch

try {
  const doc = await docsService.parseFile(slug)
} catch (e) {
  if ((e as Error).message.includes('Failed to read file stream')) {
    logger.error('Permissions/I-O issue on docs dir', { slug })
    throw new ServiceUnavailableException('Docs storage unavailable')
  }
  throw e
}

Prevention

When it happens

Trigger: File exists but is not readable by the process (permissions/ownership), the underlying storage backend (e.g. abstracted getFile) fails to open the path, or a race where the file is deleted between the stats check and the open call.

Common situations: Docs directory owned by root while the app runs as non-root, read-only filesystem mounts, NFS/object-storage-backed getFile implementations erroring, files with restrictive modes created by another user.

Related errors


AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27). Data as JSON: /api/errors/9b9e8c75cdfaa2e1. Report an issue: GitHub.