affaan-m/ECC · error · ValueError

local artifact unavailable or unsafe

Error message

local artifact unavailable or unsafe

What it means

This is the wrapper applied to any `OSError` or `AttributeError` escaping the body of `_read_local` — e.g. missing path components, permission errors, a non-existent file, `st_flags` absent on the platform, or `dir_fd`/`O_NOFOLLOW` unsupported by the OS/filesystem. The library converts these low-level failures into a single, uniform `ValueError` so callers get one predictable error for 'the artifact could not be safely read locally'.

Solutions

  1. Confirm the canonical absolute path exists and is readable: `os.path.isfile(p) and os.access(p, os.R_OK)` before calling.
  2. Catch this `ValueError`, inspect `__cause__` (the original `OSError`) to learn the real reason (ENOENT, EACCES, etc.), and fix that underlying condition.
  3. Regenerate the artifact if a cleanup/retention job deleted it, or restore it from the build output.
  4. If `AttributeError` indicates a platform limitation (`st_flags`, `O_NOFOLLOW`), run the pipeline on a filesystem/OS that supports the required fd-based safeguards, or vendor a fallback reader.

Example fix

// before
try:
    req = load_application_request(p)
except ValueError:
    pass  # reason swallowed
// after
try:
    req = load_application_request(p)
except ValueError as e:
    cause = e.__cause__  # original OSError: e.g. FileNotFoundError / PermissionError
    log.error(f"artifact {p} unreadable: {cause}")
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

import os
def assert_loadable(path: str) -> None:
    if not os.path.isabs(path):
        raise ValueError("path must be absolute")
    if not os.path.isfile(path):
        raise FileNotFoundError(path)
    if not os.access(path, os.R_OK):
        raise PermissionError(path)

Try / catch

try:
    req = load_application_request(p)
except ValueError as e:
    if str(e) == "local artifact unavailable or unsafe":
        cause = e.__cause__            # the original OSError / AttributeError
        log.error(f"cannot read {p}: {cause!r}")
        if isinstance(cause, FileNotFoundError):
            regenerate_artifact(p)
        elif isinstance(cause, PermissionError):
            fix_permissions(p)
        req = load_application_request(p)
    else:
        raise

Prevention

When it happens

Trigger: Path does not exist or an intermediate directory is missing; read permission denied; the final path component vanished between validation and open; `os.O_NOFOLLOW`/`dir_fd`/`st_flags` unavailable (platform or filesystem limitation); too many open fds causing EMFILE during open.

Common situations: Typo'd artifact path; running the tool as a user without read access to a build directory; using an old kernel or exotic filesystem (some network mounts) lacking `dir_fd` support; artifacts deleted by a cleanup job before load.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/integration.py:154

            if count > expected_size:
                raise ValueError("artifact byte count exceeded during reading")
            digest.update(data)
            if parse_json:
                chunks.append(data)
        # Rewalk the named path: a pinned old directory fd can outlive a rename.
        fresh_parent = _parent_fd(path)
        try:
            after = os.stat(path.name, dir_fd=fresh_parent, follow_symlinks=False)
        finally:
            os.close(fresh_parent)
        if (_identity(before) != _identity(os.fstat(descriptor))
                or _identity(before) != _identity(after)):
            raise ValueError("artifact changed during reading")
        if expected_hash is not None and digest.hexdigest() != expected_hash:
            raise ValueError("artifact SHA-256 mismatch")
        return _load_json(b"".join(chunks)) if parse_json else None
    except (OSError, AttributeError) as exc:
        raise ValueError("local artifact unavailable or unsafe") from exc
    finally:
        if descriptor is not None:
            os.close(descriptor)
        if parent is not None:
            os.close(parent)


def load_application_request(path: str | Path) -> dict:
    """Load only a bounded resident request; never follow a config symlink."""
    value = _read_local(str(Path(path).absolute()), parse_json=True)
    if not isinstance(value, dict):
        raise ValueError("application request must be a JSON object")
    return value


def _load_json(data: bytes) -> Any:
    def unique(pairs):
        result = {}

View on GitHub (pinned to 8321021c54)