Fix 'argument is of length zero' in R

The error argument is of length zero means if() or while() received a condition that contains no value at all: a zero-length object such as NULL, integer(0), or logical(0). It usually traces back to a misspelled column name, a missing list element, or a filter that matched nothing.

⚡ Quick Answer
if (length(x) > 0) mean(x)            # guard a possibly-empty vector
if (!is.null(x) && x > 5) "big"       # rule out NULL before comparing
if (isTRUE(x > 5)) "big"              # one call absorbs NULL, NA, empty
"profit" %in% names(df)               # df$profit NULL? check the spelling
x <- x %||% 0                         # swap NULL for a default (R 4.4+)
if (NROW(res) > 0) summary(res)       # row guard for vectors and frames

Need explanation? Read on for examples and pitfalls.

What this error means

if() needs exactly one TRUE or FALSE, and you handed it nothing. A zero-length object has no first element to test, so R stops instead of guessing. Despite the word "argument", the message is almost never about a function you wrote: the condition inside if() is technically the argument it refers to.

The shortest way to trigger it is to test NULL directly:

Run live
Run live, no install needed. Every R block on this page runs in your browser. Click Run, edit the code, re-run instantly. No setup.
RReproduce the error in one line
if (NULL) "runs" #> Error in if (NULL) "runs" : argument is of length zero

  

Real code rarely writes NULL by hand. Instead, an expression evaluates to something zero-length before it reaches if(). Comparisons keep the length of their inputs, so an empty input produces an empty result:

RWatch a comparison shrink to nothing
x <- NULL length(x) #> [1] 0 x > 5 #> logical(0)

  

logical(0) is a logical vector with zero elements. When if() receives it, there is no value to branch on, and the error fires.

Key Insight
The error names the condition's length, not your data's contents. if (x > 5) fails with "argument is of length zero" when x is empty or NULL, but with "missing value where TRUE/FALSE needed" when x is NA. The two errors point to different bugs, and the table below separates them.

The four causes and their fixes

Almost every occurrence comes from one of four patterns. Each subsection reproduces the failure, then shows the repair.

1. A misspelled column or missing list element

The $ operator returns NULL silently when the name does not exist. No warning, no error, just NULL flowing downstream until if() chokes on it:

RTypo in a column name
sales <- data.frame(region = c("east", "west"), profit = c(1200, 800)) if (sales$profitt[1] > 1000) "target met" #> Error in if (sales$profitt[1] > 1000) "target met" : argument is of length zero

  

The fix is to check the spelling against names() before trusting the column:

RConfirm the name then fix it
"profitt" %in% names(sales) #> [1] FALSE if (sales$profit[1] > 1000) "target met" #> [1] "target met"

  
Warning
$ fails silently; [[ ]] fails loudly. sales$profitt returns NULL and defers the crash to whatever runs next. sales[["profitt"]] raises an immediate "subscript out of bounds" error at the true source. Prefer [[ ]] inside functions and pipelines where a typo should surface early.

2. A function that returned NULL

An if() without an else returns NULL invisibly when the condition is false. Code that compares that return value inherits the problem:

RA branch that returns NULL
grade <- function(score) { if (score >= 50) "pass" } result <- grade(35) if (result == "pass") "celebrate" #> Error in if (result == "pass") "celebrate" : argument is of length zero

  

Give every branch an explicit value so callers always receive something testable:

RCover both branches explicitly
grade <- function(score) { if (score >= 50) "pass" else "fail" } if (grade(35) == "pass") "celebrate" else "study more" #> [1] "study more"

  

3. A subset or which() that matched nothing

which() returns integer(0) when no element satisfies the test. Indexing with an empty vector yields an empty vector, and the comparison collapses:

RAn empty match feeds the condition
temps <- c(21, 24, 19) i <- which(temps > 30) i #> integer(0) if (temps[i] > 25) "heatwave" #> Error in if (temps[i] > 25) "heatwave" : argument is of length zero

  

Guard on the count of matches before using them:

RGuard on the match count
if (length(i) > 0 && temps[i] > 25) "heatwave" else "normal range" #> [1] "normal range"

  

The && operator short-circuits: when length(i) > 0 is FALSE, R never evaluates the risky right-hand side.

4. Shiny inputs that are NULL at startup

Two of the top ten search results for this error are Shiny threads, because reactive inputs are NULL for a moment before the UI delivers a value. A plain list reproduces the mechanics:

RSimulate an unset Shiny input
input <- list(bins = NULL) if (input$bins > 10) "fine-grained" #> Error in if (input$bins > 10) "fine-grained" : argument is of length zero

  

Inside a real Shiny server, put req(input$bins) at the top of the reactive so it waits for the value. In plain R, the same defense is an explicit NULL check:

RCheck NULL before comparing
if (!is.null(input$bins) && input$bins > 10) "fine-grained" else "waiting for input" #> [1] "waiting for input"

  

What if() accepts: the full picture

Zero-length is only one of several condition values that stop if(). Knowing which message maps to which input turns a cryptic error into a direct pointer at the bug:

Condition value What happens Error message
TRUE or FALSE Branch runs normally none
NULL, logical(0), integer(0) Error argument is of length zero
NA Error missing value where TRUE/FALSE needed
c(TRUE, FALSE) (length > 1) Error since R 4.2.0 the condition has length > 1
"TRUE" or "T" Coerced, branch runs none
5 (nonzero number) Coerced to TRUE, runs none

The NA row is this error's closest sibling. An empty condition and a missing condition look similar in source code but fail differently:

RCompare the sibling NA error
if (NA) "runs" #> Error in if (NA) "runs" : missing value where TRUE/FALSE needed

  

Zero-length means the value is absent from the object entirely. NA means a slot exists but holds an unknown. Fixing the first is about where the value got lost; fixing the second is about handling missing data.

Note
Coming from Python? if None: is legal in Python and simply takes the false branch. R refuses to guess, which is why the same lookup-miss pattern that runs quietly in Python raises an error in R. The strictness is deliberate: silent false branches hide bugs.

Write conditions that never break

Three defensive patterns make a condition safe against NULL, empty vectors, and NA at once. Use them at the boundaries of your code, where values arrive from files, user input, or someone else's functions.

isTRUE() is the broadest guard. It returns TRUE only for a single TRUE, and FALSE for everything else, including all the values that would crash if():

RisTRUE absorbs every bad condition
isTRUE(NULL > 5) #> [1] FALSE isTRUE(NA > 5) #> [1] FALSE isTRUE(10 > 5) #> [1] TRUE

  

For replacing NULL with a fallback, base R 4.4.0 added the %||% operator (older versions can use if (is.null(x)) default else x, or the same operator from rlang):

RDefault a NULL with the null coalescing operator
config <- list(retries = NULL) retries <- config$retries %||% 3 retries #> [1] 3

  

For "did I get any rows back" checks, NROW() works uniformly on vectors, data frames, and NULL, so one guard covers every return type:

ROne row guard for any object
NROW(NULL) #> [1] 0 NROW(data.frame(x = 1:3)) #> [1] 3

  
Tip
Wrap conditions in isTRUE() whenever the input is not fully under your control. if (isTRUE(x > 5)) never throws, no matter what x turns out to be. The cost is that NULL and NA silently take the false branch, so reserve it for cases where "absent" and "no" should behave the same.

Try it yourself

Try it: The list ex_prices holds two stock prices. Look up "google" safely: store the price in ex_price if the name exists, and 0 if the lookup returns NULL. Print ex_price.

RYour turn: a NULL-safe lookup
ex_prices <- list(apple = 210, msft = 415) ex_price <- # your code here ex_price #> Expected: 0

  
Click to reveal solution
RSolution
ex_prices <- list(apple = 210, msft = 415) ex_price <- if (is.null(ex_prices$google)) 0 else ex_prices$google ex_price #> [1] 0

  

Explanation: ex_prices$google returns NULL because the name does not exist, and is.null() catches it before any comparison runs. In R, if is an expression, so its result assigns directly to ex_price.

FAQ

Why does R say "argument" when I am not calling a function?

Because if is a function under the hood. The parser rewrites if (cond) x into a call where the condition is the first argument, and the error message describes that argument. So "argument is of length zero" translates to "the expression inside if() evaluated to a zero-length object". The wording confuses almost everyone the first time.

Is this the same as "missing value where TRUE/FALSE needed"?

No, and the difference matters for debugging. "Argument is of length zero" means the condition has no elements at all, typically from NULL or an empty subset. "Missing value where TRUE/FALSE needed" means the condition has exactly one element and it is NA. The first sends you hunting for a lost value; the second for missing data in a value that arrived.

How do I find which line causes it in a long script?

Run traceback() immediately after the error to see the call stack, innermost call first. The top entries show the exact if() that failed. Then print the condition's ingredients with length() and is.null() right before that line. In RStudio, setting "Debug > On Error > Break in Code" drops you into the failing frame automatically.

Is this the same as "attempt to use zero-length variable name"?

No. That error comes from the parser, not from if(). It fires when code contains an empty name, most often a stray pair of backticks, an empty string in assign(""), or an imported data frame whose column header is blank. "Argument is of length zero" happens later, at evaluation, when a condition turns out to be empty. Fix the first by finding the empty name; fix the second with the guards above.

Can this error happen in a while loop?

Yes, while() applies the same rule as if(): its condition must evaluate to a single TRUE or FALSE. A loop like while (x > 0) fails with "argument is of length zero" the moment x becomes NULL or empty, for example after a subset that removed every element. The fixes are identical: guard with length(), is.null(), or isTRUE().

Start with the parent guide, common R errors and how to read them. The official R control-flow documentation specifies exactly what if() and while() accept. For the building blocks, see control flow in R for if, else, and while mechanics, R lists for why $ returns NULL on missing names, subsetting in R for how empty indexes produce empty results, and object not found for the error you get when the variable itself, not its value, is missing.