{"record":{"id":"f5cb170dbd02184c","repo":"apache/cassandra","slug":"can-t-open-r-for-reading-s","errorCode":null,"errorMessage":"Can't open %r for reading: %s","messagePattern":"Can't open %r for reading: (.+?)","errorType":"exception","errorClass":"IOError","httpStatus":null,"severity":"error","filePath":"pylib/cqlshlib/copyutil.py","lineNumber":897,"sourceCode":"        self.skip_rows = options.copy['skiprows']\n        self.fname = fname\n        self.sources = None  # might be initialised directly here? (see CASSANDRA-17350)\n        self.num_sources = 0\n        self.current_source = None\n        self.num_read = 0\n\n    @staticmethod\n    def get_source(paths):\n        \"\"\"\n         Return a source generator. Each source is a named tuple\n         wrapping the source input, file name and a boolean indicating\n         if it requires closing.\n        \"\"\"\n        def make_source(fname):\n            try:\n                return open(fname, 'r')\n            except IOError as e:\n                raise IOError(\"Can't open %r for reading: %s\" % (fname, e))\n\n        for path in paths.split(','):\n            path = path.strip()\n            if os.path.isfile(path):\n                yield make_source(path)\n            else:\n                result = glob.glob(path)\n                if len(result) == 0:\n                    raise IOError(\"Can't open %r for reading: no matching file found\" % (path,))\n\n                for f in result:\n                    yield make_source(f)\n\n    def start(self):\n        self.sources = self.get_source(self.fname)\n        self.next_source()\n\n    @property","sourceCodeStart":879,"sourceCodeEnd":915,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/pylib/cqlshlib/copyutil.py#L879-L915","documentation":"When COPY FROM opens one of the comma-separated input files, an IOError from open() is re-raised with a clearer message naming the file and the underlying OS reason. It wraps the raw errno error so the user knows which path failed.","triggerScenarios":"COPY FROM 'file.csv' where the path exists in os.path.isfile (so it passed the isfile check or was produced by glob) but open('r') still raises IOError — e.g. permission denied, a race where the file is deleted between the isfile check and open, or a directory-like special file.","commonSituations":"File owned by another user / no read permission (common when cqlsh runs as a different user than the one who created the export), file deleted by a concurrent job, path on an unmounted or network volume.","solutions":["Check and fix file permissions (chmod/chown) so the cqlsh process can read the file","Verify the file still exists and the path is correct at the time of the COPY","Run cqlsh as a user with read access, or copy the file somewhere readable"],"exampleFix":"// before\nCOPY t FROM '/root/export/data.csv';  # permission denied\n// after\n$ chmod o+r /root/export/data.csv\nCOPY t FROM '/root/export/data.csv';","handlingStrategy":"try-catch","validationCode":"import os\nfor p in path.split(','):\n    p = p.strip()\n    if not (os.path.isfile(p) and os.access(p, os.R_OK)):\n        raise SystemExit(f\"Cannot read {p}\")","typeGuard":null,"tryCatchPattern":"try:\n    run_copy_from(fname)\nexcept IOError as e:\n    print(f\"Input file unreadable: {e}\")  # fix perms/path before retrying","preventionTips":["Check file readability with os.access before COPY FROM","Run cqlsh as the same user that owns the export files","Avoid deleting/moving files while a COPY is running"],"tags":["cqlsh","copy","io","file"],"backgroundTag":"file-open-failed","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}