apache/cassandra · error · IOError

Can't open %r for reading

Error message

Can't open %r for reading: %s

What it means

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.

Solutions

  1. Check and fix file permissions (chmod/chown) so the cqlsh process can read the file
  2. Verify the file still exists and the path is correct at the time of the COPY
  3. Run cqlsh as a user with read access, or copy the file somewhere readable

Example fix

// before
COPY t FROM '/root/export/data.csv';  # permission denied
// after
$ chmod o+r /root/export/data.csv
COPY t FROM '/root/export/data.csv';
Defensive patterns

Strategy: try-catch

Validate before calling

import os
for p in path.split(','):
    p = p.strip()
    if not (os.path.isfile(p) and os.access(p, os.R_OK)):
        raise SystemExit(f"Cannot read {p}")

Try / catch

try:
    run_copy_from(fname)
except IOError as e:
    print(f"Input file unreadable: {e}")  # fix perms/path before retrying

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/f5cb170dbd02184c. Report an issue: GitHub.

Appendix: source

Thrown at pylib/cqlshlib/copyutil.py:897

        self.skip_rows = options.copy['skiprows']
        self.fname = fname
        self.sources = None  # might be initialised directly here? (see CASSANDRA-17350)
        self.num_sources = 0
        self.current_source = None
        self.num_read = 0

    @staticmethod
    def get_source(paths):
        """
         Return a source generator. Each source is a named tuple
         wrapping the source input, file name and a boolean indicating
         if it requires closing.
        """
        def make_source(fname):
            try:
                return open(fname, 'r')
            except IOError as e:
                raise IOError("Can't open %r for reading: %s" % (fname, e))

        for path in paths.split(','):
            path = path.strip()
            if os.path.isfile(path):
                yield make_source(path)
            else:
                result = glob.glob(path)
                if len(result) == 0:
                    raise IOError("Can't open %r for reading: no matching file found" % (path,))

                for f in result:
                    yield make_source(f)

    def start(self):
        self.sources = self.get_source(self.fname)
        self.next_source()

    @property

View on GitHub (pinned to 88fd0f6a0e)