Panniantong/Agent-Reach · error · PrivatePathError

读取目标不是常规文件:{target}

Error message

读取目标不是常规文件:{target}

What it means

Raised by the O_NOFOLLOW private file reader when the opened file descriptor's fstat shows the target is not a regular file (not S_ISREG). The file was opened with O_NOFOLLOW so a symlink final component would have failed at open; this check catches everything else: directories, device nodes, FIFOs, sockets. Message (Chinese): 'read target is not a regular file: {target}'.

Source

Thrown at agent_reach/utils/paths.py:164

    if max_bytes < 0:
        raise ValueError("max_bytes must be non-negative")

    target = ensure_no_symlink_path(path, "读取路径")
    flags = (
        os.O_RDONLY
        | getattr(os, "O_NOFOLLOW", 0)
        | getattr(os, "O_NONBLOCK", 0)
        | getattr(os, "O_CLOEXEC", 0)
    )
    try:
        fd = os.open(target, flags)
    except FileNotFoundError:
        return None

    try:
        file_stat = os.fstat(fd)
        if not stat.S_ISREG(file_stat.st_mode):
            raise PrivatePathError(f"读取目标不是常规文件:{target}")
        if file_stat.st_size > max_bytes:
            raise PrivatePathError(f"读取目标超过大小上限:{target}")

        chunks = []
        remaining = max_bytes + 1
        while remaining:
            chunk = os.read(fd, remaining)
            if not chunk:
                break
            chunks.append(chunk)
            remaining -= len(chunk)
        payload = b"".join(chunks)
        if len(payload) > max_bytes:
            raise PrivatePathError(f"读取目标超过大小上限:{target}")
        ensure_no_symlink_path(target, "读取路径")
    finally:
        os.close(fd)
    return payload.decode(encoding)

View on GitHub (pinned to 93ae1d18c3)

Solutions

  1. Check the caller: ensure you pass a file path, not its parent directory
  2. Verify with Path(target).is_file() (or os.path.isfile) before invoking the read helper in your own glue code
  3. If a FIFO/socket sits at the expected file path, remove it and let the app recreate the real file

Example fix

# before
data = read_private_file("~/.config/agent-reach")  # that's the directory

# after
data = read_private_file("~/.config/agent-reach/credentials.yaml")
Defensive patterns

Strategy: type-guard

Validate before calling

import os

def is_regular_file(path: str) -> bool:
    try:
        return os.path.isfile(path)  # True only for regular files (follows no special nodes)
    except (OSError, ValueError):
        return False

Type guard

import stat, os

def is_regular_file_strict(path: str) -> bool:
    """True when path exists and is a regular file (not dir/fifo/socket/device)."""
    try:
        return stat.S_ISREG(os.stat(path).st_mode)
    except OSError:
        return False

Try / catch

from agent_reach.utils.paths import PrivatePathError
try:
    payload = read_private(target, max_bytes=cap)
except PrivatePathError as e:
    if "常规文件" in str(e):
        target = find_real_file(target)  # e.g. append the expected filename
        payload = read_private(target, max_bytes=cap)
    else:
        raise

Prevention

When it happens

Trigger: Passing a directory path, a named pipe, /dev/null or another device node to the private read helper (the function that takes max_bytes). Happens when a glob or user input yields a non-file path.

Common situations: Passing a config/cache path that is actually a directory (e.g. the private dir itself instead of a file inside it); Unix tools creating FIFOs at expected file locations; glob patterns matching sockets in runtime dirs.

Related errors


AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14). Data as JSON: /api/errors/8554fb3ca8bfe3a0. Report an issue: GitHub.