Appendix C — Debugging

C.1 When code does not work

Errors are a normal part of learning R. The goal is not to memorize every message. It is to follow the same small set of checks each time.

C.2 Errors, warnings, and messages

  • An error stops the code.
  • A warning allows the code to finish but reports a possible problem.
  • A message gives information about what a function did.
  • An unexpected result may appear without any warning, so always inspect the output.
Read before changing

Read the final line of an error and identify the object, column, or function it mentions. Do not add several new functions at once in the hope that the error will disappear.

C.3 A simple debugging routine

  1. Run the code again and read the complete message.
  2. Check the spelling and capitalization of object and column names.
  3. Confirm that the required package was loaded.
  4. Inspect the table with glimpse().
  5. Run one pipeline step at a time.
  6. Change one thing, then run the code again.

C.4 Example: a column name is wrong

library(tidyverse)

holdings <- tibble(
  company = c("A Ltd", "B Ltd", "C Ltd"),
  market_value_nok = c(120, 80, 100)
)

This code asks for a column that does not exist:

holdings |>
  arrange(desc(market_value))
Error in `arrange()`:
ℹ In argument: `..1 = market_value`.
Caused by error:
! object 'market_value' not found

glimpse() shows the exact column names:

glimpse(holdings)
Rows: 3
Columns: 2
$ company          <chr> "A Ltd", "B Ltd", "C Ltd"
$ market_value_nok <dbl> 120, 80, 100

Make the smallest correction:

holdings |>
  arrange(desc(market_value_nok))

C.5 Example: find the first bad step

Break a longer pipeline into small objects:

step_1 <- holdings |>
  filter(market_value_nok >= 100)

step_2 <- step_1 |>
  mutate(value_millions = market_value_nok / 1000000)

step_1
step_2

The code runs, but the second result is unexpectedly small. The toy values were not measured in full kroner. This is a unit problem, not a syntax problem. Code that runs can still be wrong.

C.6 Check missing values with familiar functions

Use summarise() to display simple checks. is.na() identifies missing values, so sum(is.na(company)) counts missing company names:

holdings |>
  summarise(
    rows = n(),
    missing_company_names = sum(is.na(company)),
    smallest_value = min(market_value_nok)
  )

The expected results are three rows, no missing company names, and a positive smallest value. A useful check begins with a reasonable expectation about the source.

C.7 Ask for help effectively

Include:

  1. what you expected;
  2. what happened;
  3. the exact error or warning;
  4. the smallest example that reproduces it; and
  5. the packages being used.

Do not share confidential data or passwords when asking for help.

C.8 Practice

Find and correct the two problems in this code:

story <- tibble(
  outlet = c("A", "B", "C"),
  articles = c(12, NA, 20)
)

story |>
  filter(Article >= 15) |>
  summarise(average = mean(articles))
Error in `filter()`:
ℹ In argument: `Article >= 15`.
Caused by error:
! object 'Article' not found

Explain the column-name problem and decide how the missing value should be handled. Do not add na.rm = TRUE until you can explain what the NA means.

C.9 Takeaways

Practice Why it helps
Read the message Identifies the immediate problem
Check spelling and case Finds many object and column errors
glimpse() Shows names, types, and example values
Run one step at a time Locates the first step that changes unexpectedly
Change one thing Makes cause and effect easier to understand
Display simple checks Catches results that are possible in R but implausible in the story