jackwener/OpenCLI · error · CliError

FILE_READ_ERROR

FILE_READ_ERROR

Error message

Unable to inspect file: ${resolvedPath}

What it means

readPdfFile() calls fs.stat() to inspect the candidate PDF before reading it. If stat() fails for any reason other than ENOENT (which produces the dedicated FILE_NOT_FOUND 'File not found' error), the library wraps it as FILE_READ_ERROR 'Unable to inspect file: <path>'. This means the filesystem could not be queried at all — typically a permissions problem, a broken symlink loop, or a path component that is not a directory.

Source

Thrown at clis/paperreview/utils.js:67

export async function readPdfFile(inputPath) {
    const rawPath = trimOrEmpty(inputPath);
    if (!rawPath) {
        throw new CliError('ARGUMENT', 'A PDF path is required.', 'Provide a local PDF file path');
    }
    const resolvedPath = path.resolve(rawPath);
    const fileName = path.basename(resolvedPath);
    if (!fileName.toLowerCase().endsWith('.pdf')) {
        throw new CliError('ARGUMENT', 'The input file must end with .pdf.', 'Provide a PDF file path');
    }
    let fileStat;
    try {
        fileStat = await fs.stat(resolvedPath);
    }
    catch (error) {
        if (error?.code === 'ENOENT') {
            throw new CliError('FILE_NOT_FOUND', `File not found: ${resolvedPath}`, 'Provide a valid PDF file path');
        }
        throw new CliError('FILE_READ_ERROR', `Unable to inspect file: ${resolvedPath}`, 'Check file permissions and try again');
    }
    if (!fileStat.isFile()) {
        throw new CliError('FILE_NOT_FOUND', `Not a file: ${resolvedPath}`, 'Provide a valid PDF file path');
    }
    if (fileStat.size < 100) {
        throw new CliError('ARGUMENT', 'The PDF is too small. paperreview.ai requires at least 100 bytes.', 'Provide the final paper PDF');
    }
    if (fileStat.size > MAX_PDF_BYTES) {
        throw new CliError('FILE_TOO_LARGE', 'The PDF is larger than paperreview.ai\'s 10MB limit.', 'Compress the PDF or submit a smaller file');
    }
    let buffer;
    try {
        buffer = await fs.readFile(resolvedPath);
    }
    catch {
        throw new CliError('FILE_READ_ERROR', `Unable to read file: ${resolvedPath}`, 'Check file permissions and try again');
    }
    return {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check permissions with ls -l on the file and every parent directory; chmod/chown so the running user can access the path
  2. Verify the path is valid: ensure no path component is a regular file and the path has no symlink loop (namei -l <path>)
  3. Run stat <path> yourself to reproduce the underlying errno and fix accordingly
  4. If on a CI runner, confirm the workspace/files were checked out with readable permissions

Example fix

// before
await readPdfFile(process.argv[2]);
// after
import { accessSync, constants } from 'node:fs';
const p = process.argv[2];
accessSync(p, constants.R_OK); // throws a clear EACCES before the CLI call
await readPdfFile(p);
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
import path from 'node:path';
function canInspect(p) {
    try {
        const resolved = path.resolve(p);
        fs.accessSync(resolved, fs.constants.R_OK | fs.constants.F_OK);
        return true;
    } catch (e) {
        if (e.code === 'ENOENT') return false; // different error path
        return false; // EACCES/ELOOP/ENOTDIR etc. -> would raise FILE_READ_ERROR
    }
}

Type guard

function isRegularFileStat(s) {
    return typeof s === 'object' && s !== null && typeof s.isFile === 'function' && s.isFile();
}

Try / catch

try {
    const pdf = await readPdfFile(userPath);
} catch (e) {
    if (e?.code === 'FILE_READ_ERROR' && e.message.startsWith('Unable to inspect file')) {
        console.error(`Cannot access ${userPath}: check permissions and that no parent path component is a file.`);
    } else throw e;
}

Prevention

When it happens

Trigger: Calling readPdfFile() when fs.stat(resolvedPath) rejects with something other than ENOENT: EACCES (no permission on the file or a parent directory), ELOOP (symlink loop), ENOTDIR (a path component is a file, e.g. /path/to.pdf/sub.pdf), or filesystem I/O errors.

Common situations: Running the CLI as a user lacking read permission on the file or a parent directory; passing a path under a symlink loop; passing a path whose parent component is actually a file; read-protected directories on shared CI runners.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/5fbcca0f76cac160. Report an issue: GitHub.