{"record":{"id":"7fde9fb5e67cc82a","repo":"usestrix/strix","slug":"failed-to-read-target-list-file-path-str-e-s","errorCode":null,"errorMessage":"Failed to read target list file '{path_str}': {e!s}","messagePattern":"Failed to read target list file '(.+?)': (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"strix/interface/utils.py","lineNumber":1227,"sourceCode":"def read_target_list_file(path_str: str) -> list[str]:\n    \"\"\"Read scan targets from a file, one target per non-empty, non-comment line.\"\"\"\n    if not path_str or not path_str.strip():\n        raise ValueError(\"--target-list path must not be empty.\")\n\n    path = Path(path_str).expanduser()\n    if not path.is_file():\n        raise ValueError(f\"Target list file '{path_str}' is not an existing file.\")\n\n    try:\n        targets = [\n            target\n            for line in path.read_text(encoding=\"utf-8\").splitlines()\n            if (target := line.strip()) and not target.startswith(\"#\")\n        ]\n    except UnicodeDecodeError as e:\n        raise ValueError(f\"Target list file '{path_str}' must be valid UTF-8 text: {e!s}\") from e\n    except OSError as e:\n        raise ValueError(f\"Failed to read target list file '{path_str}': {e!s}\") from e\n\n    targets = [target for target in targets if target]\n    if not targets:\n        raise ValueError(f\"Target list file '{path_str}' is empty.\")\n    return targets\n\n\ndef sanitize_name(name: str) -> str:\n    sanitized = re.sub(r\"[^A-Za-z0-9._-]\", \"-\", name.strip())\n    return sanitized or \"target\"\n\n\ndef derive_repo_base_name(repo_url: str) -> str:\n    if repo_url.endswith(\"/\"):\n        repo_url = repo_url[:-1]\n\n    if \":\" in repo_url and repo_url.startswith(\"git@\"):\n        path_part = repo_url.split(\":\", 1)[1]","sourceCodeStart":1209,"sourceCodeEnd":1245,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/interface/utils.py#L1209-L1245","documentation":"Catch-all for OSError raised while reading the target list file (after existence was confirmed) — permission errors, files deleted between the is_file() check and the read, or I/O failures on flaky mounts. The original OSError is chained as the cause, so the message includes the OS-level detail.","triggerScenarios":"read_target_list_file succeeds the is_file() check, then path.read_text raises PermissionError (mode 000 file), or the containing directory loses read/execute permission, or the network share drops mid-read.","commonSituations":"Files owned by another user in shared CI runners; security software locking the file; race with a concurrent process deleting the list.","solutions":["Inspect the chained cause in the traceback for the OS errno.","Fix permissions: chmod 644 targets.txt and ensure the parent dir is traversable.","Move the list to local disk if it sits on an unreliable network mount."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import os\n\ndef is_readable_file(p: str) -> bool:\n    try:\n        with open(p, encoding=\"utf-8\"):\n            return True\n    except OSError:\n        return False\n\nif not is_readable_file(path_str):\n    raise SystemExit(f\"Cannot read {path_str}: check permissions and mounts\")","typeGuard":null,"tryCatchPattern":"try:\n    targets = read_target_list_file(path_str)\nexcept ValueError as e:\n    if \"Failed to read target list file\" in str(e):\n        raise SystemExit(f\"OS error reading {path_str}: {e.__cause__}\") from e\n    raise","preventionTips":["Check file permissions (chmod 644) before automated runs","Keep input files on local disk rather than flaky network shares"],"tags":["filesystem","permissions","targets","environment"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}