{"record":{"id":"9ebeac434b5533a1","repo":"affaan-m/ECC","slug":"artifact-must-be-a-resident-regular-file","errorCode":null,"errorMessage":"artifact must be a resident regular file","messagePattern":"artifact must be a resident regular file","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/integration.py","lineNumber":123,"sourceCode":"            parent = child\n        return parent\n    except BaseException:\n        os.close(parent)\n        raise\n\n\ndef _read_local(raw: str, *, parse_json: bool, expected_size: int | None = None,\n                expected_hash: str | None = None) -> Any:\n    path = Path(raw)\n    if not path.is_absolute() or str(path) != raw or \"..\" in path.parts:\n        raise ValueError(\"artifact path must be canonical and absolute\")\n    parent = descriptor = None\n    try:\n        flags = os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK\n        parent = _parent_fd(path)\n        before = os.stat(path.name, dir_fd=parent, follow_symlinks=False)\n        if not stat.S_ISREG(before.st_mode) or getattr(before, \"st_flags\", 0) & 0x40000000:\n            raise ValueError(\"artifact must be a resident regular file\")\n        if expected_size is None:\n            expected_size = before.st_size\n        if parse_json and expected_size > _MAX_JSON:\n            raise ValueError(\"JSON artifact exceeds local size limit\")\n        if before.st_size != expected_size:\n            raise ValueError(\"artifact byte count mismatch\")\n        descriptor = os.open(path.name, flags, dir_fd=parent)\n        if _identity(before) != _identity(os.fstat(descriptor)):\n            raise ValueError(\"artifact changed before reading\")\n        digest, chunks, count = hashlib.sha256(), [], 0\n        while data := os.read(descriptor, 65536):\n            count += len(data)\n            if count > expected_size:\n                raise ValueError(\"artifact byte count exceeded during reading\")\n            digest.update(data)\n            if parse_json:\n                chunks.append(data)\n        # Rewalk the named path: a pinned old directory fd can outlive a rename.","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/integration.py#L105-L141","documentation":"After lstat-ing the target without following symlinks, `_read_local` verifies it is a regular file and that no sticky pinned/immutable-style flag (`0x40000000`, checked via `st_flags`) is set. Anything else — a directory, FIFO, device node, socket, symlink, or a file with the offending flag — is rejected so the reader only consumes ordinary resident regular files that the identity-based TOCTOU checks can reason about.","triggerScenarios":"Pointing `_artifact` or `load_application_request` at a directory, a symlink, a named pipe, `/dev/null`, or a file whose `st_flags` include bit `0x40000000` (e.g. certain pinned/immutable marker flags on BSD/macOS filesystems).","commonSituations":"A typo where the path points at the output directory instead of the file inside it; a stale symlink left by a previous build; creating artifacts via `mkfifo` for streaming; filesystem-specific flag setting from backup or dedup tools.","solutions":["Ensure the path names a real regular file: `Path(p).is_file()` (after resolving symlinks) before calling.","Replace symlinks with hard copies of the artifact, or resolve the symlink and pass the final target path (which must still be absolute and canonical).","Inspect the file's flags (`ls -lO` on macOS/BSD) and clear the pinned/immutable bit (`chflags nouchg <file>`) if it was set by another tool.","Regenerate the artifact if it was produced as a special file (pipe/device) instead of a regular file on disk."],"exampleFix":"// before\nartifact_path = \"/out/latest\"  # actually a symlink chain ending in a directory\n// after\nimport os, stat\ninfo = os.lstat(artifact_path)\nassert stat.S_ISREG(info.st_mode) and not (getattr(info, 'st_flags', 0) & 0x40000000)\nartifact_path = os.path.realpath(artifact_path)","handlingStrategy":"validation","validationCode":"import os, stat\ndef assert_readable_regular_file(path: str) -> None:\n    info = os.stat(path, follow_symlinks=False)\n    if not stat.S_ISREG(info.st_mode):\n        raise ValueError(f\"not a regular file: {path}\")\n    if getattr(info, \"st_flags\", 0) & 0x40000000:\n        raise ValueError(f\"file has pinned/immutable flag set: {path}\")","typeGuard":"def is_plain_regular_file(path: str) -> bool:\n    import os, stat\n    try:\n        info = os.stat(path, follow_symlinks=False)\n    except OSError:\n        return False\n    return stat.S_ISREG(info.st_mode) and not (getattr(info, \"st_flags\", 0) & 0x40000000)","tryCatchPattern":"try:\n    req = load_application_request(p)\nexcept ValueError as e:\n    if str(e) == \"artifact must be a resident regular file\":\n        target = os.path.realpath(p)\n        clear_pinned_flags(target)          # e.g. chflags nouchg on macOS/BSD\n        req = load_application_request(target)\n    else:\n        raise","preventionTips":["Check `Path(p).is_file()` after resolving symlinks before pointing the loader at a path.","Copy artifacts out of symlinked directories instead of referencing symlinks directly.","Avoid special-file outputs (FIFOs, devices) for artifacts; always materialize regular files.","Check file flags (`ls -lO`) when a previously working artifact suddenly fails this check."],"tags":["filesystem","file-type","security"],"backgroundTag":"incompatible-source-type","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}