R Error: 'could not find function', Package Not Loaded or Name Conflict?
Error: could not find function "X" means R walked every loaded package and the global environment without finding anything named X. The fix is almost always one of three things: load the package that owns the function, call it explicitly as package::function(), or correct a typo or masking conflict that is hiding it.
What does "could not find function" actually mean?
R resolves every function name by walking an ordered chain of environments called the search path. If nothing along that chain defines the name, you get this error. The fastest way to make the error stop feeling mysterious is to reproduce it on purpose, catch it with tryCatch(), and then print the chain R actually searched.
R started at .GlobalEnv (your workspace), then tried each attached package in load order, then base. some_made_up_fn does not exist in any of those, so the lookup fails and R raises the error. Every "could not find function" error is just this same walk coming up empty, the question becomes why the name you expected isn't on the chain.
Try it: Use search() and length() to count how many environments are currently on the search path. Save the number to ex_n_pkgs.
Click to reveal solution
Explanation: search() returns a character vector of every environment on the lookup chain, so length() gives you the exact number R has to check before raising the error.
How do I find which package a function belongs to?
Once you know the search path, the next question is: where does this function actually live? R has two small built-in helpers, find() and getAnywhere(), that tell you, and apropos() handles the case where you only remember part of the name.
find("mean") only scans the search path, so it will return character(0) for a function whose package you haven't loaded. getAnywhere("lowess") scans installed namespaces too, which is why it finds lowess even when stats is not attached in your particular session. apropos() takes a regular expression and lists every matching name, invaluable when you typed Read.csv and can't remember whether the real function is capitalised.
??"linear model" queries the help index across every installed package and returns ranked matches, so you don't need to guess a function name to start searching.Try it: Call find() on "sd" and save the result to ex_sd_pkg.
Click to reveal solution
Explanation: sd() is exported by the stats package, which is attached by default in every R session, so find() reports it as package:stats.
Why does loading one package break a function from another?
When two packages export a function with the same name, the later-loaded one masks the earlier one. Your old code that called the first version now silently runs the second version, or crashes if the signatures differ. You can reproduce the exact same mechanic in a single session without loading anything, by defining a shadow function in the global environment.
The first call to mean(1:5) returns the string because .GlobalEnv sits at the front of the search path and R's walk stops there. find("mean") then shows both matches, in the order R would visit them. base::mean(1:5) bypasses the walk entirely and asks the base namespace directly, and rm(mean) removes the shadow so normal lookup resumes. Real package masking works exactly the same way, the only difference is that the masking function lives in package:dplyr or similar instead of .GlobalEnv.
dplyr::filter masking stats::filter. Check conflicts() whenever a familiar function suddenly behaves strangely.Try it: Define a broken shadow of sum that always returns 0, call it on 1:10, then compute the real total with base::sum() and save it to ex_total.
Click to reveal solution
Explanation: The shadow sum lives in .GlobalEnv and is found first. base::sum(1:10) skips the search path and calls the base namespace directly, so the real sum comes through.
When should I use package::function() instead of library()?
The :: operator reaches straight into a package's namespace without attaching it to the search path. That makes it the right tool for one-off calls, for disambiguating masked functions, and for scripts you want to be self-documenting. Use library() when you need dozens of functions from the same package; use :: when you only need one or when the reader should see exactly where a function came from.
Each :: call is resolved at the moment it runs, so it never pollutes your search path and never changes the behavior of unrelated code. The last block also shows an important debugging signal: if you see there is no package called 'X' instead of could not find function "foo", you are looking at a different problem, the package is not installed at all, and the fix is install.packages("X"), not library(X).
:: gives exported functions; ::: reaches internal ones and is discouraged. Internal functions are not part of a package's public contract, so they can change or disappear between versions without warning. Only use ::: when you are debugging the package itself.Try it: Use base::nrow() to get the row count of mtcars without loading anything, and save it to ex_mt_rows.
Click to reveal solution
Explanation: base::nrow(mtcars) calls nrow from the base namespace directly, bypassing any function with the same name that might be on the search path.
What's a fast debug checklist when this error hits?
When the error appears in a script you cannot easily read line-by-line, run through these five questions in order. The first one whose answer is "no" is almost always the cause. R gives you the primitives to check each one in a single line.
Run the block above with fn and pkg set to whatever the error message said, and the failing check points straight at the cause. If step 1 fails the name is wrong; if step 2 fails you need install.packages(); if step 3 fails you need library() or ::; if step 4 fails something is masking the function and you need package::function(); if step 5 returns near-misses you probably hit a renamed function from a package update.
:: in any script you share. It removes every bit of ambiguity about which package owns which function and makes the code robust to whatever load order the next reader's session happens to use.Try it: Check whether the tibble package is currently loaded in your session and save the logical result to ex_has_tibble.
Click to reveal solution
Explanation: loadedNamespaces() returns the names of every package whose namespace is loaded into this session. Membership testing with %in% gives you a single TRUE/FALSE answer.
Practice Exercises
Exercise 1: Write a missing-function diagnoser
Build a function dx_missing(name) that takes a function name as a string and returns a character vector. The first element is a verdict, one of "ok", "installed-but-not-loaded", or "not-found", and any remaining elements are up to five near-miss candidates from apropos(). Test it on "mean", "Read.csv", and "zzz_nope" and save the last result to my_dx.
Click to reveal solution
Explanation: exists(name, mode = "function") answers whether the exact name is bound to a function anywhere on the search path. apropos() with a short prefix and ignore.case = TRUE catches both capitalisation typos (Read.csv → read.csv) and genuinely missing names with similar spellings.
Exercise 2: Detect every masking source for a name
Write mask_check(fn_name) that returns a character vector of every location currently offering a function with the given name, in search-path order. Use getAnywhere() and look at its $where component. Test it on "filter" after first defining a local filter <- function(x) head(x, 3), save to my_mask, then clean up with rm(filter).
Click to reveal solution
Explanation: getAnywhere() scans both the search path and installed namespaces, so its $where field is exactly the list of places the name currently lives. Before rm(filter) there are two separate bindings competing; after, only the stats version remains.
Complete Example: Debugging a broken script end-to-end
A reader's script fails with could not find function "filter". The fix is a three-step walk: diagnose, try the wrong fix, then pick the right one. Below is the same reasoning chain in code.
Two namespaces on this machine export filter: stats (the attached one) and dplyr (installed but not loaded). The reader wanted dplyr's data-frame filter, not the stats time-series filter, so the right fix is dplyr::filter, not stats::filter.
One :: call resolves the whole thing: no load order to worry about, no masking surprises if the script grows later, and the next reader can see at a glance exactly which filter was intended.
Summary
| Cause | How to detect | Fix |
|---|---|---|
| Typo in function name | exists("name", mode = "function") is FALSE, apropos() shows near-misses |
Correct the spelling; R is case-sensitive |
| Package not installed | "pkg" %in% rownames(installed.packages()) is FALSE |
install.packages("pkg") |
| Package installed but not loaded | "pkg" %in% loadedNamespaces() is FALSE |
library(pkg) or pkg::function() |
| Masked by another package | length(find("name")) > 1 |
package::function() to disambiguate |
| Renamed in package update | apropos() shows a similarly named function |
Check the package changelog for the new name |
References
- R Core Team. An Introduction to R, chapter on "The R environment". cran.r-project.org/doc/manuals/r-release/R-intro.html
- Wickham, H. Advanced R, 2nd edition, chapter 7, "Environments". adv-r.hadley.nz/environments.html
- R documentation,
?search,?find,?apropos,?conflicts,?getAnywhere. rdocumentation.org - R Core Team. Writing R Extensions, section 1.6, "Package namespaces". cran.r-project.org/doc/manuals/r-release/R-exts.html
- tidyverse blog, "Loading packages and function masking". tidyverse.org/blog
- Stack Overflow canonical question, "Error: could not find function" in R. stackoverflow.com/q/7027288
Continue Learning
- R Common Errors, the full reference covering every error message R throws and how to read it.
- R Error: object 'x' not found, the sibling error for variables rather than functions, with the same search-path intuition.
- R Functions, how R defines, stores, and resolves functions, including first-class function semantics.