666ghj/MiroFish · error · StarHistoryError

Star count file is not a regular file

Error message

Star count file is not a regular file

What it means

After a successful O_NOFOLLOW open, load_star_count_file fstats the descriptor and requires a regular file (stat.S_ISREG). FIFOs, sockets, character/block devices, and directories are rejected so the reader never blocks on a fifo or trust device semantics for a tiny decimal count.

Source

Thrown at scripts/star_history.py:523

    if len(payload) > limit:
        raise StarHistoryError(f"{label} exceeded the size limit")
    return payload


def load_star_count_file(path: Path) -> int:
    """Read a tiny, symlink-safe decimal count produced by the fetch-only step."""

    flags = os.O_RDONLY
    if hasattr(os, "O_NOFOLLOW"):
        flags |= os.O_NOFOLLOW
    try:
        descriptor = os.open(path, flags)
    except OSError as exc:
        raise StarHistoryError("Star count file is missing or unsafe") from exc
    try:
        metadata = os.fstat(descriptor)
        if not stat.S_ISREG(metadata.st_mode):
            raise StarHistoryError("Star count file is not a regular file")
        payload = os.read(descriptor, MAX_COUNT_FILE_BYTES + 1)
    except OSError as exc:
        raise StarHistoryError("could not read Star count file") from exc
    finally:
        os.close(descriptor)

    if len(payload) > MAX_COUNT_FILE_BYTES:
        raise StarHistoryError("Star count file exceeded the size limit")
    if not re.fullmatch(rb"(?:0|[1-9][0-9]*)\n?", payload):
        raise StarHistoryError("Star count file must contain one decimal integer")
    count = int(payload)
    if count > MAX_STAR_COUNT:
        raise StarHistoryError("Star count exceeded the supported range")
    return count


def load_state(workspace: Path, require_canonical: bool = True) -> dict[str, Any]:
    state_path = _safe_target(workspace, STATE_RELATIVE, create_parent=False)

View on GitHub (pinned to b5b53acc57)

Solutions

  1. stat the path: it should be '-rw-' not 'p','s','c','b', or 'd'
  2. Remove the non-regular file: rm / path-to-object
  3. Re-run the fetch step to write a plain regular file
  4. If this appears in tests, assert the fixture creates a file, not a directory

Example fix

# before
os.mkfifo(count_path)          # test fixture mistake
# after
count_path.write_text('1234\n')  # regular file
Defensive patterns

Strategy: type-guard

Validate before calling

import stat, os
st = os.lstat(p)
if not stat.S_ISREG(st.st_mode):
    raise RuntimeError(f'count path is not a regular file: mode {oct(st.st_mode)}')

Type guard

def is_regular_file(p: Path) -> bool:
    try:
        return stat.S_ISREG(os.lstat(p).st_mode)
    except OSError:
        return False

Prevention

When it happens

Trigger: The path handed to load_star_count_file is a named pipe (mkfifo), a unix socket, /dev-style device node, or a directory. Note O_NOFOLLOW only guards the final symlink hop, so other non-regular types reach this check.

Common situations: A stale fifo left by earlier tooling at the count path, tests using tmp_path fixtures that create directories where a file is expected, or deliberately crafted inputs in security review.

Related errors


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/62164b130772b7ae. Report an issue: GitHub.