affaan-m/ECC · error · RuntimeError

secure output requires O_NOFOLLOW and O_DIRECTORY

Error message

secure output requires O_NOFOLLOW and O_DIRECTORY

What it means

_SafeOutput implements a secure output tree using dir_fd-relative, no-follow file operations, which require the platform's os.O_NOFOLLOW and os.O_DIRECTORY flags. If either attribute is missing (unsupported or emulated platform), __init__ raises immediately rather than silently falling back to symlink-vulnerable path handling. This is an intentional hard failure to preserve the symlink-attack guarantees of the writer.

Solutions

  1. Run the workflow on Linux/macOS where O_NOFOLLOW and O_DIRECTORY exist
  2. Upgrade Python to a build that exposes these os flags on the target platform
  3. If you control the code, gate _SafeOutput usage behind a platform check and provide an alternate (less strict) output writer for unsupported platforms
  4. Avoid emulated Windows environments (MSYS/older WSL) for this workflow

Example fix

# before
out = _SafeOutput(Path('out'))  # RuntimeError on Windows

# after
if not hasattr(os, 'O_NOFOLLOW') or not hasattr(os, 'O_DIRECTORY'):
    raise SystemExit('tasteforge secure output requires a POSIX platform (Linux/macOS)')
out = _SafeOutput(Path('out'))
Defensive patterns

Strategy: try-catch

Validate before calling

import os
platform_ok = hasattr(os, 'O_NOFOLLOW') and hasattr(os, 'O_DIRECTORY')
if not platform_ok:
    raise SystemExit('This workflow requires a POSIX platform (Linux/macOS).')

Type guard

def supports_secure_output() -> bool:
    return hasattr(os, 'O_NOFOLLOW') and hasattr(os, 'O_DIRECTORY')

Try / catch

try:
    out = _SafeOutput(root)
except RuntimeError as e:
    if 'O_NOFOLLOW' in str(e):
        sys.exit('Unsupported platform: secure output needs O_NOFOLLOW/O_DIRECTORY')
    raise

Prevention

When it happens

Trigger: Instantiating _SafeOutput(root) on a Python build/platform lacking os.O_NOFOLLOW or os.O_DIRECTORY (e.g. Windows, some embedded or exotic platforms where os does not expose these flags).

Common situations: Running the tasteforge workflow on Windows or a minimal cross-compiled Python; very old Python versions; restricted sandboxes that strip os constants.

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/2344beb83d3286d6. Report an issue: GitHub.

Appendix: source

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

    samples = measured.get("style_samples", [])
    if not isinstance(samples, list) or any(
        not isinstance(sample, dict)
        or not _finite_real(sample.get("time"))
        or float(cast(float, sample["time"])) < 0
        or float(cast(float, sample["time"])) > float(duration)
        for sample in samples
    ):
        raise ValueError("reference style evidence times must be finite and within duration")
    return float(duration)


class _SafeOutput:
    """Descriptor-bound output tree with no-follow traversal and atomic writes."""

    def __init__(self, root: Path) -> None:
        self._root_fd = -1
        if not hasattr(os, "O_NOFOLLOW") or not hasattr(os, "O_DIRECTORY"):
            raise RuntimeError("secure output requires O_NOFOLLOW and O_DIRECTORY")
        if root.exists() or root.is_symlink():
            metadata = root.lstat()
            if stat.S_ISLNK(metadata.st_mode):
                raise ValueError("output root must not be a symlink")
            if not stat.S_ISDIR(metadata.st_mode):
                raise ValueError("output root must be a directory")
        else:
            if not root.parent.is_dir():
                raise ValueError("output parent directory must already exist")
            root.mkdir(mode=0o700)
        self.root = root
        self._root_fd = os.open(root, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
        self._written: list[str] = []

    def close(self) -> None:
        if self._root_fd >= 0:
            os.close(self._root_fd)
            self._root_fd = -1

View on GitHub (pinned to 8321021c54)