affaan-m/ECC · error · ValueError

stable source probing requires O_NOFOLLOW

Error message

stable source probing requires O_NOFOLLOW

What it means

This error is raised by _stable_probe when the running Python interpreter's os module does not expose O_NOFOLLOW, a flag only available on POSIX systems (Linux, macOS, BSDs). O_NOFOLLOW is essential to this workflow because probing a 'stable source' requires opening the file without following symlinks, so the digest is bound to one physical source object. Without the flag the function cannot make that security guarantee, so it refuses to run rather than silently probing an unsafe path. Windows and very old/limited Python builds lack os.O_NOFOLLOW.

Solutions

  1. Run the workflow on a POSIX platform (Linux/macOS/BSD) where os.O_NOFOLLOW exists.
  2. Verify with `python -c "import os; print(hasattr(os, 'O_NOFOLLOW'))"` before running; if False, switch interpreters or platforms.
  3. If Windows support is required, resolve `path.resolve(strict=True)` and verify it is not a symlink before reading, accepting the weaker guarantee instead of O_NOFOLLOW.

Example fix

// before (Windows: os has no O_NOFOLLOW)
python workflow.py probe source.py

// after (run on Linux/macOS or check capability first)
import os
assert hasattr(os, "O_NOFOLLOW"), "run on a POSIX platform"
python workflow.py probe source.py
Defensive patterns

Strategy: validation

Validate before calling

import os
if not hasattr(os, "O_NOFOLLOW"):
    raise RuntimeError("tasteforge stable probing requires a POSIX platform with O_NOFOLLOW")

Type guard

def supports_o_nofollow() -> bool:
    import os
    return hasattr(os, "O_NOFOLLOW")

Prevention

When it happens

Trigger: Calling _stable_probe (directly or via run_workflow) on a platform whose os module lacks O_NOFOLLOW — typically Windows (no O_NOFOLLOW in os) or a stripped/embedded POSIX Python build.

Common situations: Running the tasteforge workflow on Windows or WSL1 configurations, in a CI container with a minimal/odd Python runtime, or after a port that replaced os with a shim lacking O_NOFOLLOW.

Understand the failure class

Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/e40801cc71a7382f. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/workflow.py:62


def _hash_descriptor(descriptor: int) -> tuple[int, str]:
    os.lseek(descriptor, 0, os.SEEK_SET)
    digest = hashlib.sha256()
    total = 0
    while True:
        chunk = os.read(descriptor, 1024 * 1024)
        if not chunk:
            break
        total += len(chunk)
        digest.update(chunk)
    return total, digest.hexdigest()


def _stable_probe(path: Path, probe: Probe) -> tuple[dict[str, Any], int, str]:
    """Probe a private snapshot while binding the digest to one stable source object."""
    if not hasattr(os, "O_NOFOLLOW"):
        raise ValueError("stable source probing requires O_NOFOLLOW")
    descriptor = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
    try:
        before = os.fstat(descriptor)
        if not stat.S_ISREG(before.st_mode):
            raise ValueError("reference source must be a regular file")
        with tempfile.TemporaryDirectory(prefix="tasteforge-source-") as temporary:
            snapshot = Path(temporary) / f"source{path.suffix}"
            snapshot_fd = os.open(
                snapshot, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600
            )
            try:
                os.lseek(descriptor, 0, os.SEEK_SET)
                digest = hashlib.sha256()
                total = 0
                while True:
                    chunk = os.read(descriptor, 1024 * 1024)
                    if not chunk:
                        break

View on GitHub (pinned to 8321021c54)