tidyverse/ggplot2

`ggplot2` problem whe using `parse()` in `scale_x_continuous(labels = ...)` throws error despite matching label/break lengths

Closed this issue · 5 comments

Description

When using the parse() function to add math annotation in the labels argument of scale_x_continuous(), I encounter an error in ggplot2 version 4.0.0. This occurs even though the number of breaks and labels is the same.

Minimal Reproducible Example

library(tidyverse)

ggplot(tibble(x = -10:10, y = x^2)) +
  geom_point(aes(x, y)) +
  scale_x_continuous(
    breaks = seq(-10, 10, 2),
    labels = parse(text = paste0(seq(-10, 10, 2), "^degree"))
  )


# Confirming breaks length
length(seq(-10, 10, 2))  # Returns: 11

# Confirming labels length
length(paste0(seq(-10, 10, 2), "^degree"))  # Returns: 11

It always result in the following message.

Error in `scale_x_continuous()`:
! `breaks` and `labels` have different lengths.

Below my sessionInfo()

R version 4.5.1 (2025-06-13 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26100)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=English_United States.utf8  LC_CTYPE=English_United States.utf8    LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C                           LC_TIME=English_United States.utf8    

time zone: America/New_York
tzcode source: internal

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

other attached packages:
 [1] lubridate_1.9.4 forcats_1.0.0   stringr_1.5.2   dplyr_1.1.4     purrr_1.1.0     readr_2.1.5     tidyr_1.3.1    
 [8] tibble_3.3.0    tidyverse_2.0.0 ggplot2_4.0.0  

loaded via a namespace (and not attached):
 [1] vctrs_0.6.5        cli_3.6.5          rlang_1.1.6        stringi_1.8.7      generics_0.1.4     S7_0.2.0          
 [7] labeling_0.4.3     glue_1.8.0         ggh4x_0.3.1.9000   hms_1.1.3          scales_1.4.0       grid_4.5.1        
[13] tzdb_0.5.0         lifecycle_1.0.4    compiler_4.5.1     RColorBrewer_1.1-3 timechange_0.3.0   pkgconfig_2.0.3   
[19] rstudioapi_0.17.1  farver_2.1.2       R6_2.6.1           dichromat_2.0-0.1  tidyselect_1.2.1   pillar_1.11.0     
[25] magrittr_2.0.4     tools_4.5.1        withr_3.0.2        gtable_0.3.6   

Thanks, confirmed.

It seems the problem is that size0 returns NULL for expressions.

ggplot2/R/scale-.R

Lines 1179 to 1184 in eece200

if (!identical(size0(labels), size0(breaks))) {
cli::cli_abort(
"{.arg breaks} and {.arg labels} have different lengths.",
call = self$call
)
}

labels <- parse(text = paste0(seq(-10, 10, 2), "^degree"))
ggplot2:::size0(labels)
#> NULL

Created on 2025-09-21 with reprex v2.1.1

Probably, the fix would be to easy to add || is.expression(x) to the second if condition.

ggplot2/R/utilities.R

Lines 803 to 811 in eece200

size0 <- function(x) {
if (obj_is_vector(x)) {
vec_size(x)
} else if (is.vector(x)) {
length(x)
} else {
NULL
}
}

Note that you can also use math_format() to format the break values as expressions.

library(tibble)
library(ggplot2)

ggplot(tibble(x = -10:10, y = x^2)) +
  geom_point(aes(x, y)) +
  scale_x_continuous(
    breaks = seq(-10, 10, 2),
    labels = scales::math_format(.x^degree)
  )

Created on 2025-09-21 with reprex v2.1.1

Thank you so much for your response and comments.

Thanks for reporting!

Thank you. And it is already fixed. It broke ggsurveillance::label_power10() and it took me a while to track this down to size0.