R Error: 'cannot open the connection', File Path Checklist That Fixes It
Error in file(file, "rt") : cannot open the connection means R tried to open a file or URL and failed. Nine times out of ten the file is fine, your working directory points somewhere else, the filename has a typo, or a Windows backslash mangled the path. Walk the checklist below and you'll find the cause in under a minute.
What does "cannot open the connection" actually mean?
The error message looks scary because it says "connection," but in R-speak a "connection" just means the open handle to a file or URL. R never got that far. Reproducing the error and printing one line of diagnostic info is enough to know which checklist item to chase next.
The error message itself tells you nothing actionable, it just says "I tried, it failed." The three diagnostic lines below tell you everything: where R was looking, whether the file is there, and the absolute path R resolved your input to. With those three facts, every cause in this checklist becomes a 30-second fix.
http:// URL, this error is always a path problem. Stop checking your wifi.Try it: Create a real file with tempfile() + writeLines(), then re-run the diagnostic block on its path. Watch file.exists() flip from FALSE to TRUE.
Click to reveal solution
Explanation: tempfile() only reserves a path, it doesn't create the file. The writeLines() call is what actually puts bytes on disk, which is why file.exists() flips from FALSE to TRUE only after the write.
Is your working directory where you think it is?
This is cause number one, the file you wrote is real and lives somewhere on disk, but R is looking in a different folder. Relative paths like "data.csv" are resolved against R's current working directory, and that directory is rarely what you assume.
Two lines do all the work. getwd() tells you where R is looking; list.files() shows what R can actually see from there. If the file you want isn't in that listing, R isn't going to find it with a relative path no matter how many times you re-run read.csv().
There's a sneaky variant of this bug. Inside an R Markdown or Quarto document, the working directory of each chunk is the document's folder, not the project root. A path that works in the console fails when you knit. The portable fix is here::here("data", "myfile.csv"), it builds an absolute path anchored to your project root, regardless of where the chunk runs.
setwd() inside scripts you'll share. Hard-coded paths break for every collaborator. Use RStudio Projects (which set the wd automatically) or here::here() (which anchors paths to the project root, not the current chunk).Try it: Build an absolute path with here::here() (mocked here as a file.path() call since here isn't preloaded in WebR) for a file at data/sales.csv under a project root of /projects/analysis.
Click to reveal solution
Explanation: here::here() walks up from the current file until it finds a project marker (.Rproj, .git, etc.) and joins your arguments with the right separator for the OS. file.path() is the underlying primitive that handles separators correctly on every platform.
Does the file exist exactly where you say it does?
The working directory checks out, but R still can't find the file. Time to check whether the name matches what R sees on disk. Three things go wrong here: typos, hidden file extensions, and case sensitivity.
file.exists() is the truth oracle, if it returns FALSE, R will refuse to open the file, full stop. The follow-up list.files(pattern = ...) is the diagnostic: it shows you every candidate so you can spot the typo (mtdata.csv vs mydata.csv), the wrong extension (.csv vs .tsv), or a case mismatch (Data.csv vs data.csv).
Two platform-specific gotchas catch beginners constantly. On Linux and macOS, filenames are case-sensitive, so Data.csv and data.csv are two different files. On Windows, File Explorer hides known extensions by default, so a file you see as data.csv may actually be data.csv.txt on disk, meaning R has to be told to open data.csv.txt, not data.csv.
data.csv in Explorer may be data.csv.txt on disk. Never trust File Explorer's filename, always confirm with list.files() from R.Try it: Write a real temp file, then call file.exists() on it AND on a similarly-named-but-typo path. Confirm one returns TRUE and the other FALSE.
Click to reveal solution
Explanation: file.exists() is a hard, OS-level test. It doesn't care about your intentions, it tells you whether this exact byte sequence points to a real entry on disk. Use it as the first line of defence before any read.* call.
Are Windows backslashes breaking your path?
Windows uses \ as its path separator, but R uses \ as its string escape character. So when you paste a Windows path straight into a string literal, R thinks \U, \n, and \t are escape sequences, and either parses your path wrong or refuses to parse it at all. There are three clean fixes.
All four lines produce paths Windows can resolve to the same file. forward is the easiest to write and works identically on Linux, macOS, and Windows. doubled is what you'll see in older code. The r"(...)" raw string is the R 4.0+ feature that lets you paste a Windows path directly from File Explorer without changing a single character, by far the friendliest fix for occasional copy-pasters. And file.path() is the right tool whenever you're building a path from variables instead of writing it as a literal.
r"(...)") let you paste a Windows path verbatim. No escaping, no doubling, no editing. If you copy a path from File Explorer and wrap it in r"(...)", it just works. This alone has saved more "cannot open the connection" hours than any other R 4.x feature.Try it: Build a Windows-style absolute path to report.xlsx in C:\Users\you\Reports using file.path().
Click to reveal solution
Explanation: file.path() joins its arguments with the platform's separator (/ on Linux/macOS, also accepted on Windows). On Windows, R uses forward slashes internally even when the OS prefers backslashes, both work for reading.
Is something else holding the file open or denying access?
You've confirmed the path is right and the file exists, but R still can't read it. This is the "permission" branch of the checklist. The file is locked by another program, the OS denies your user read access, or the drive containing it has gone offline.
file.access() uses Unix-style return codes, 0 means "yes, you have this permission" and -1 means "no, you don't." It's the only base-R function that distinguishes "the file is missing" from "the file is locked or denied," which is exactly the distinction you need when file.exists() returns TRUE but read.csv() still fails.
Three real-world causes account for almost every permission failure: the file is open in Excel (Windows takes an exclusive lock on .xlsx and .csv files), the path is on a mapped network drive that has disconnected, or the file lives in a protected system directory like C:\Program Files. Close the file, reconnect the drive, or move the file to your home folder respectively.
.xlsx and .csv files while open. R will get a permission-denied error even though the file clearly exists. Close the file in Excel before reading from R, or, better, copy it to a temp folder first so you can keep working in Excel without blocking the script.Try it: Call file.access() with read mode on a path that doesn't exist. Confirm it returns -1, not 0.
Click to reveal solution
Explanation: file.access() returns -1 for any failure mode, missing file, denied permission, or broken symlink. Pair it with file.exists() to disambiguate: if file.exists() is TRUE but file.access(., 4) == -1, you know it's a permission problem, not a typo.
How do I diagnose every cause in one shot?
Six checklist items add up to a lot of typing. The fix is to wrap them in a single function and call it the moment the error appears. The function below prints the input, the resolved path, existence, parent-directory contents, read permission, and a one-line verdict, every checklist item in 15 lines.
Every line of output corresponds to a checklist item from the previous sections. You instantly see which check failed and which one to fix. The "Parent dir" listing is the workhorse, it's where you spot the typo, the wrong extension, or the missing folder. Once Verdict reads OK to read, your read.csv() call will succeed.
diagnose_path() in your .Rprofile so it's available in every R session. The first time you hit "cannot open the connection" after adding it, you'll wonder how you ever lived without it.Try it: Call diagnose_path() on a fresh tempfile() path (the path is reserved but no file exists yet) and read the verdict.
Click to reveal solution
Explanation: tempfile() reserves a unique path inside tempdir() but never creates the file, so file.exists() is FALSE. The verdict line confirms what the checklist would have told you the slow way.
Practice Exercises
Exercise 1: Build a safe read.csv() wrapper
Write safe_read_csv(path) that reads the CSV if it exists, but if the path is missing it should print every same-extension file in the parent directory (so the user can spot the typo) and return NULL. Combine file.exists(), dirname(), tools::file_ext(), and list.files().
Click to reveal solution
Explanation: The wrapper short-circuits to read.csv() on the happy path, so it costs nothing when the file exists. When the file is missing, tools::file_ext() extracts the extension, dirname() finds the parent, and list.files() shows every same-extension sibling, usually exposing the typo immediately.
Exercise 2: Search a list of candidate directories
Write try_paths(name, dirs) that takes a filename and a vector of candidate directories, and returns the first existing path (or NULL if none match). Useful when a file might live in data/, inputs/, or a network share, and you don't want to handcode the lookup.
Click to reveal solution
Explanation: Vectorising file.exists() over the candidate paths is faster and cleaner than a for loop. Returning the first hit (instead of all of them) gives the function deterministic semantics, the order of dirs becomes a priority list.
Complete Example
Pull every checklist item together: write a CSV from mtcars, then read it back through diagnose_path() so the diagnostic confirms each step before the read happens. This is the pattern to use any time you're about to do a read.* call you don't trust.
The diagnostic block reads OK to read because every checklist item passes: working directory is sane, the path resolves to a real file, file.exists() is TRUE, file.access() confirms read permission, and the parent directory contains exactly the file we wrote. With every check green, read.csv() cannot fail with "cannot open the connection." If it ever does, one of the earlier diagnostic lines would have caught it first.
Summary
| Cause | How to detect | Fix | Prevention |
|---|---|---|---|
| Wrong working directory | getwd() shows an unexpected folder |
setwd() once, or use absolute paths |
Use RStudio Projects + here::here() |
| Filename typo / case mismatch | file.exists() is FALSE, list.files() shows the real name |
Correct the spelling or extension | Tab-complete file names in RStudio |
| Hidden Windows extension | list.files() shows data.csv.txt instead of data.csv |
Use the real on-disk name | Show file extensions in Explorer |
| Backslash escape collision | String parses to wrong path or fails | Forward slashes, doubled backslashes, or r"(...)" |
Use file.path() to build paths |
| File locked / permission denied | file.exists() is TRUE but file.access(., 4) == -1 |
Close the file in Excel, reconnect drive, move file | Don't read directly from C:\Program Files |
References
- R Core Team, Connections documentation. Link
- R Core Team,
?file.existsreference. Link - R Core Team,
?file.accesspermission codes. Link herepackage documentation, Müller, K. Link- Bryan, J., Project-oriented workflow. tidyverse blog. Link
- R 4.0.0 release notes, raw string literals. Link
Continue Learning
- R Common Errors, the parent reference covering 50 common R errors with fixes.
- R Error: object 'x' not found, the variable-scoping cousin of this error.
- R Error in read.csv: more columns than column names, the next error you'll hit once
read.csv()actually opens the file.