Error in if: the condition has length > 1.

When loading my shiny application, for the output of ggplotly, the shinyapps.io log gives me the following message: Error in if: the condition has length > 1.

I have checked why this message occurs and I saw that it is associated with the use of if(), but that it is solved with ifelse(); in my code, I only use ifelse(), but to generate the output of ggplotly, I do not use ifelse(), so I do not understand where the error can be.

I am grateful if you can guide me to solve this.

1 Like

Hi, welcome!

To help us help you, could you please prepare a reproducible example (reprex) illustrating your issue? Please have a look at these resources, to see how to create one for a shiny app

I'm getting exactly this same error, Lekmonm!
Have you ever find out what causes it and how to solve it ?

Feel free to share your own reproducible example of the issue.

Hi, you probably have a logical vector with length > 1 (as a condition), try all() or any() or something similar.

Thank you for caring, nirgrahamuk, but for a newcomer to both R and Shiny, making my first app is challenging enough, I would probably waste too much time trying to build a reprex. If I don't find a solution I'll try it! Thanks

Hi, I met the same error, and came here after googling.

For future reference, below I write the reason and workarounds I found for my case.

In my case, the reason is I gave multiple elements in a vector as an argument of xlab (and ylab) of ggplot2.
ggplot2 does not complain anything and silently processes successfully the first element in the vector as the label as if a single value is given as a label, whereas ggplotly dies with this Error in if: the condition has length > 1 error which is originated in gg2list.

My first quick workaround is to add unique() to the arguments of xlab and ylab. Also in my case the labels are indeed coming from input values, so I can simply replace them to the input values.

A reprex is as below. I also put my two ways of fix below by the ways as described above.

  • Reprex of error.
library(shiny)

library(ggplot2)
library(plotly)


### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
# UI Side ----
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###

ui <- fluidPage(
  selectInput("label_x", "My label for x-axis", c('my own1', 'my own2')), 
  
  h4("Plot by ggplotly"), 
  # plotlyOutput("ggplotly_ok_wo_xlab"), 
  plotlyOutput("ggplotly_notok"), 
  # plotlyOutput("ggplotly_ok_way1"), 
  # plotlyOutput("ggplotly_ok_way2"), 
  
  h4("Plot by ggplot"), 
  plotOutput("ggplot")
)

### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
# Server Side ----
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###

server <- function(input, output, session) {
  
    # Construction of ggplot object g is the same in both output$ggplot and output$ggplotly_notok. 
    #  Still, ggplot is ok whereas ggplotly_notok dies with message 'Error in if: the condition has length > 1'. 
  output$ggplot <- renderPlot({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    # not necessarily the same length as the n_row of data: 
    # xlabs_my <- rep(label_x, 3)
    # indeed `ggplot` silently picks up the first element even when different values are given: 
    # xlabs_my <- c('my1', 'my 2')
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      xlab(paste0('my xlab is: ', xlabs_my))
    
    g
  })
  
  
  output$ggplotly_ok_way1 <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      xlab(unique(paste0('my xlab is: ', xlabs_my)))
    # xlab(paste0('my xlab is: ', xlabs_my))
    
    ggplotly(g)
  })
  
  output$ggplotly_ok_way2 <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    # xlabs_my <- rep(label_x, dim(faithful)[1])
    xlab_my <- paste0('(which is taken directly from input): ', label_x)
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      xlab(paste0('my xlab is: ', xlab_my))
      # xlab(unique(paste0('my xlab is: ', xlabs_my)))
    # xlab(paste0('my xlab is: ', xlabs_my))
    
    ggplotly(g)
  })
  
    # Construction of ggplot object g is the same in both output$ggplot and output$ggplotly_notok. 
    #  Still, ggplot is ok whereas ggplotly_notok dies with message 'Error in if: the condition has length > 1'. 
  output$ggplotly_notok <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      # xlab(unique(paste0('my xlab is: ', xlabs_my)))
      xlab(paste0('my xlab is: ', xlabs_my))
    
    ggplotly(g)
  })

    # output$ggplotly_ok_wo_xlab, which is without the part of xlab addition, works fine. 
  output$ggplotly_ok_wo_xlab <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    # g <- g +
    #   # xlab(unique(paste0('my xlab is: ', xlabs_my)))
    #   xlab(paste0('my xlab is: ', xlabs_my))
    
    # print(ggplot2::last_plot())
    
    ggplotly(g)
  })

  # stat_density_2d(aes(fill = ..level..), geom = "polygon") + 
  # Warning: The dot-dot notation (`..level..`) was deprecated in ggplot2 3.4.0.
  # ℹ Please use `after_stat(level)` instead.
  # ℹ The deprecated feature was likely used in the ggplot2 package.
  # Please report the issue at <https://github.com/tidyverse/ggplot2/issues>.
}

# Run the application 
shinyApp(ui = ui, server = server)
  • The error message with the above app.R
Warning: Error in if: the condition has length > 1
  104: gg2list
  103: ggplotly.ggplot
  101: renderPlotly [<mytopdir>/app.R#113]
  100: func
   97: shinyRenderWidget
   96: func
   83: renderFunc
   82: output$ggplotly_notok
    1: shiny::runApp
  • Workaround 1, use unique()
library(shiny)

library(ggplot2)
library(plotly)


### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
# UI Side ----
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###

ui <- fluidPage(
  selectInput("label_x", "My label for x-axis", c('my own1', 'my own2')), 
  
  h4("Plot by ggplotly"), 
  # plotlyOutput("ggplotly_notok"), 
  plotlyOutput("ggplotly_ok_way1"), 
  # plotlyOutput("ggplotly_ok_way2"), 
  
  h4("Plot by ggplot"), 
  plotOutput("ggplot")
)

### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
# Server Side ----
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###

server <- function(input, output, session) {
  
  output$ggplot <- renderPlot({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    # not necessarily the same length as the n_row of data: 
    # xlabs_my <- rep(label_x, 3)
    # indeed `ggplot` silently picks up the first element even when different values are given: 
    # xlabs_my <- c('my1', 'my 2')
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      xlab(paste0('my xlab is: ', xlabs_my))
    
    g
  })
  
  
  output$ggplotly_ok_way1 <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      xlab(unique(paste0('my xlab is: ', xlabs_my)))
    # xlab(paste0('my xlab is: ', xlabs_my))
    
    ggplotly(g)
  })
  
  output$ggplotly_ok_way2 <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    # xlabs_my <- rep(label_x, dim(faithful)[1])
    xlab_my <- paste0('(which is taken directly from input): ', label_x)
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      xlab(paste0('my xlab is: ', xlab_my))
      # xlab(unique(paste0('my xlab is: ', xlabs_my)))
    # xlab(paste0('my xlab is: ', xlabs_my))
    
    ggplotly(g)
  })
  
  output$ggplotly_notok <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      # xlab(unique(paste0('my xlab is: ', xlabs_my)))
      xlab(paste0('my xlab is: ', xlabs_my))
    
    ggplotly(g)
  })
  # stat_density_2d(aes(fill = ..level..), geom = "polygon") + 
  # Warning: The dot-dot notation (`..level..`) was deprecated in ggplot2 3.4.0.
  # ℹ Please use `after_stat(level)` instead.
  # ℹ The deprecated feature was likely used in the ggplot2 package.
  # Please report the issue at <https://github.com/tidyverse/ggplot2/issues>.
}

# Run the application 
shinyApp(ui = ui, server = server)
  • Workaround 2, taken from input
library(shiny)

library(ggplot2)
library(plotly)


### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
# UI Side ----
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###

ui <- fluidPage(
  selectInput("label_x", "My label for x-axis", c('my own1', 'my own2')), 
  
  h4("Plot by ggplotly"), 
  # plotlyOutput("ggplotly_notok"), 
  # plotlyOutput("ggplotly_ok_way1"), 
  plotlyOutput("ggplotly_ok_way2"), 
  
  h4("Plot by ggplot"), 
  plotOutput("ggplot")
)

### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
# Server Side ----
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###

server <- function(input, output, session) {
  
  output$ggplot <- renderPlot({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    # not necessarily the same length as the n_row of data: 
    # xlabs_my <- rep(label_x, 3)
    # indeed `ggplot` silently picks up the first element even when different values are given: 
    # xlabs_my <- c('my1', 'my 2')
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      xlab(paste0('my xlab is: ', xlabs_my))
    
    g
  })
  
  
  output$ggplotly_ok_way1 <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      xlab(unique(paste0('my xlab is: ', xlabs_my)))
    # xlab(paste0('my xlab is: ', xlabs_my))
    
    ggplotly(g)
  })
  
  output$ggplotly_ok_way2 <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    # xlabs_my <- rep(label_x, dim(faithful)[1])
    xlab_my <- paste0('(which is taken directly from input): ', label_x)
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      xlab(paste0('my xlab is: ', xlab_my))
      # xlab(unique(paste0('my xlab is: ', xlabs_my)))
    # xlab(paste0('my xlab is: ', xlabs_my))
    
    ggplotly(g)
  })
  
  output$ggplotly_notok <- renderPlotly({
    label_x <- input$label_x
    
    # dim(faithful)
    # [1] 272   2
    xlabs_my <- rep(label_x, dim(faithful)[1])
    
    g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
      stat_density_2d(aes(fill = after_stat(level)), geom = "polygon") +
      xlim(1, 6) + ylim(40, 100)
    
    g <- g +
      # xlab(unique(paste0('my xlab is: ', xlabs_my)))
      xlab(paste0('my xlab is: ', xlabs_my))
    
    ggplotly(g)
  })
  # stat_density_2d(aes(fill = ..level..), geom = "polygon") + 
  # Warning: The dot-dot notation (`..level..`) was deprecated in ggplot2 3.4.0.
  # ℹ Please use `after_stat(level)` instead.
  # ℹ The deprecated feature was likely used in the ggplot2 package.
  # Please report the issue at <https://github.com/tidyverse/ggplot2/issues>.
}

# Run the application 
shinyApp(ui = ui, server = server)
  • sessionInfo()
> library(shiny)
> 
> library(ggplot2)
> library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:
  
  last_plot

The following object is masked from ‘package:stats’:
  
  filter

The following object is masked from ‘package:graphics’:
  
  layout

> sessionInfo()
R version 4.3.2 (2023-10-31)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Sonoma 14.4.1

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

locale:
  [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Asia/Tokyo
tzcode source: internal

attached base packages:
  [1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
  [1] plotly_4.10.4 ggplot2_3.5.0 shiny_1.8.0  

loaded via a namespace (and not attached):
  [1] jsonlite_1.8.8    gtable_0.3.4      dplyr_1.1.4       compiler_4.3.2    promises_1.2.1    tidyselect_1.2.0 
[7] Rcpp_1.0.12       tidyr_1.3.1       later_1.3.2       scales_1.3.0      fastmap_1.1.1     mime_0.12        
[13] R6_2.5.1          labeling_0.4.3    generics_0.1.3    isoband_0.2.7     htmlwidgets_1.6.4 MASS_7.3-60.0.1  
[19] tibble_3.2.1      munsell_0.5.0     pillar_1.9.0      rlang_1.1.3       utf8_1.2.4        httpuv_1.6.14    
[25] lazyeval_0.2.2    viridisLite_0.4.2 cli_3.6.2         withr_3.0.0       magrittr_2.0.3    digest_0.6.34    
[31] grid_4.3.2        rstudioapi_0.15.0 xtable_1.8-4      lifecycle_1.0.4   vctrs_0.6.5       data.table_1.15.0
[37] glue_1.7.0        farver_2.1.1      fansi_1.0.6       colorspace_2.1-0  purrr_1.0.2       httr_1.4.7       
[43] tools_4.3.2       pkgconfig_2.0.3   ellipsis_0.3.2    htmltools_0.5.7  
> 

Using a reprex is how you help the folks on this site you want help from to understand exactly what your issue is. It just takes a minute to learn how to do and is nowhere near as complicated as trying build an app. It's just a more friendly way to copy and paste the code that isn't working for you and the error messages it produces. Just copying and pasting will work, too, but isn't as helpful to the folks who you want help from.

For further future record, I also write here about:

  1. Note about if error I sometimes encounter.
  2. The way I tried debugging at the side of source code (although generally it is more difficult in shiny than making a minimum reprex).

--

  1. Note about if error I sometimes encounter.

As cezary.kuran suggested about a logical vector with length > 1 (as a condition), I sometimes encounter this sort of error in some tools (released before R 4.2.0 ~ R 4.3.0), where they fail with not very informative message about if error. This is originated from the change of R introduced in R 4.2.0 and R.4.3.0 as extracted below. In my reprex case, we see it should have started dying there since R 4.2.0, Calling if() or while() with a condition of length greater than one gives an error rather than a warning.

CHANGES IN R 4.3.0
...
Calling && or || with LHS or (if evaluated) RHS of length greater than one is now always an error, with a report of the form

    'length = 4' in coercion to 'logical(1)'
Environment variable _R_CHECK_LENGTH_1_LOGIC2_ no longer has any effect.
CHANGES IN R 4.2.0
...
Calling && or || with either argument of length greater than one now gives a warning (which it is intended will become an error).

Calling if() or while() with a condition of length greater than one gives an error rather than a warning. Consequently, environment variable _R_CHECK_LENGTH_1_CONDITION_ no longer has any effect.

c.f. R: R News

  1. Debugging at the side of source code.

I tried the same g object and ggplotly(g) on console with my reprex, after shiny dies about the error.

> g
> ggplotly(g)
Error in if (robust_nchar(axisTitleText) > 0) { : 
  the condition has length > 1

Here I now see a different error message instead of the original Error in if: the condition has length > 1.

After checking the source code, I found robust_nchar is the original function of plotly in utils.R:

# nchar() needs a non-empty character vector; sometimes x will be a
# factor, or an empty vector.
robust_nchar <- function(x, ...) {
  if (length(x)) nchar(as.character(x), ...)
  else 0
}

axisTitleText in ggplotly.ggmatrix is set by axisTitleText <- sc$name %||% plot$labels[[xy]] %||% "".

Instead I set axisTitleText <- g$labels[['x']] and then:

> axisTitleText <- g$labels[['x']]
> axisTitleText
[1] "my xlab is: my1"  "my xlab is: my 2"
> robust_nchar(axisTitleText)
[1] 15 16
> if (robust_nchar(axisTitleText) > 0) {print("ok")}else{print("notok")}
Error in if (robust_nchar(axisTitleText) > 0) { : 
  the condition has length > 1

So we see this hits the change since R 4.2.0.

These lines are in ggplotly_build of ggplotly.R. The comment at the beginning of ggplotly_build says :

  # ------------------------------------------------------------------------
  # Our internal version of ggplot2::ggplot_build(). Modified from
  # https://github.com/hadley/ggplot2/blob/0cd0ba/R/plot-build.r#L18-L92
  # ------------------------------------------------------------------------
  ggplotly_build <- function(p) {

As we can see here, I now understand that ggplotly uses their own derivative for some parts of ggplot, so although it processes the same g object as print.ggplot, still it can produce different consequence.