27  Troubleshooting common issues

This chapter collects the errors and warnings you are most likely to meet while adding a dataset and compiling a traits.build database, and explains how to resolve each one. It is the “detailed troubleshooting” companion to the Getting help chapter: work through the relevant section here before posting a question.

Most problems fall into a few families:

Two general habits solve a large share of issues. First, read the error message carefully — the traits.build messages are designed to name the offending trait, dataset, or value. Second, before assuming the package is at fault, check whether the problem lies in your data files or custom R code; see attribute the error in the Getting help chapter.

27.1 Unsupported trait values

This error occurs when, for a categorical trait, a value in data.csv is not one of the allowable values defined for that trait in the trait dictionary (config/traits.yml). The offending records are moved to the excluded_data table rather than silently dropped.

List the values that need a substitution:

table <- my_database$excluded_data %>%
  filter(dataset_id == current_study) %>%
  filter(error == "Unsupported trait value") %>%
  select(dataset_id, trait_name, value) %>%
  distinct()

You then map each unrecognised value onto an allowable one using a substitution. Add a single substitution to metadata.yml with metadata_add_substitution():

metadata_add_substitution(
  dataset_id = current_study,
  trait_name = "plant_growth_form",
  find = "T",
  replace = "tree"
)

When many values need fixing, edit the table above (add a replace column) and read the whole set in at once with metadata_add_substitutions_table(). The table must have the columns dataset_id, trait_name, find, and replace:

table <- table %>%
  rename(find = value) %>%
  mutate(replace = c("tree", "mallee", "shrub", "graminoid", "herb"))

metadata_add_substitutions_table(
  table,
  dataset_id = dataset_id,
  trait_name = trait_name,
  find = find,
  replace = replace
)

For a long list it is often easiest to write the table to a CSV, edit it in a spreadsheet or text editor, and read it back:

write_csv(table, "data/dataset_id/raw/substitutions_required.csv")

# ...edit outside of R...

table <- read_csv("data/dataset_id/raw/substitutions_required.csv")

See adding substitutions for the full workflow, and dataset_check_categorical_substitutions() in the data-quality functions chapter for a helper that builds the table for you.

27.2 Numeric values out of range

For a numeric trait, a value that falls outside the allowable range defined for that trait in config/traits.yml is moved to excluded_data. Inspect the excluded values:

my_database$excluded_data %>%
  filter(dataset_id == current_study,
         error == "Value out of allowable range") %>%
  select(dataset_id, trait_name, value, observation_id, unit, original_name)

If the list is long, the cause is almost always a units-conversion problem — the incoming units in the metadata do not match what the data actually contains, so every value is scaled wrongly. Check the unit_in you mapped for the trait against the units in the source, and confirm the conversion exists in config/unit_conversions.csv. If only a few values are excluded, they may be legitimately out of range; confirm they are genuine errors before dismissing them. dataset_check_numeric_values() in the data-quality functions chapter tabulates these for review.

27.3 Taxon names that don’t align

An aligned_name in a newly added dataset may not appear in the database’s config/taxon_list.csv for two reasons: the name needs aligning (a typo or non-standard syntax), or the taxon list simply doesn’t yet include that name and must be extended from an external taxonomic resource.

Each database maintains its own taxonomy — the taxon list must be built outside the traits.build workflow because different databases reference different taxonomic sources (AusTraits, for example, uses the Australian Plant Census and the Australian Plant Names Index). Generate the list of names still needing attention with dataset_check_taxonomic_updates() (see the data-quality functions chapter), then either add taxonomic updates to the dataset’s metadata.yml (see adding taxonomic updates) or extend the taxon list.

27.4 Dataset can’t pivot to wide format

To convert a traits.build database to wide format, the traits table must be able to pivot wider. One of the tests run by dataset_test()traits.build::check_pivot_wider() — confirms this. It checks that every row in the traits table has a unique combination of seven columns:

dataset_id, trait_name, observation_id, value_type, repeat_measurements_id, method_id, and method_context_id.

If two rows share the same combination, the table cannot pivot and the test fails. In almost every case the real cause is that observation_id has not been generated as you intended — two genuinely distinct observations were assigned the same observation_id because a distinguishing piece of metadata was not mapped. (See the glossary for how observation_id is built.)

There are two common explanations and solutions:

  1. Species-level values repeated alongside individual-level measurements. If your dataset mixes individual- (or population-) level measurements with species-level measurements, the same species-level value may be read in on many rows. Retain only the first instance of each species-level value using custom_R_code, where taxon_name is the column of taxon names and column 1, column 2, … are the categorical-trait columns that need de-duplicating:
data %>%
  group_by(taxon_name) %>%
  mutate(across(c("column 1", "column 2", "column 3"), replace_duplicates_with_NA)) %>%
  ungroup()
  1. Repeat measurements over time (or by method) not distinguished. Rows representing measurements made at different times, or with different methods, on the same entity need a temporal_context_id / repeat_measurements_id (for repeat measurements over time) or a method_context_id (for multiple methods) so that they resolve to distinct rows. Map the column that distinguishes them, or add the appropriate context, so each measurement is uniquely identified.

To find exactly which measurements are blocking the pivot, run dataset_check_not_pivoting() (see the data-quality functions chapter); it returns the specific rows with duplicate combinations so you can trace the missing piece of metadata.

27.5 Duplicate measurements

Duplicates come in two forms.

Within a dataset, the same taxon × trait value can legitimately appear many times — for example a species-level value (like plant_growth_form) reported on every individual’s row, or a single bulked measurement reported against every contributing individual. When such repetition is unintended, remove it with custom_R_code (see custom R code). The helper dataset_check_duplicates_within_dataset() flags suspicious within-dataset duplication — be alert when n_duplicates is large or is identical across every taxon for a trait.

Across datasets, the same measurements may be submitted twice — for instance when two collaborators contribute the same data, or when a dataset arrives both from its collector and inside a broader compilation. The automated tests eliminate most exact duplicates, but cross-dataset detection is genuinely hard (it depends on how many significant figures to compare, and traits with naturally narrow ranges produce false positives). See dataset_check_duplicates_across_datasets() in the data-quality functions chapter for the current status of this check.

27.6 Errors reading metadata.yml or traits.yml

Both the dataset metadata files and the trait dictionary are YAML files, where meaning is carried entirely by indentation and structure. Small formatting slips produce errors that can look alarming but are quick to fix. The most common are:

  • incomplete final line found on '...' — the file is missing a trailing newline, or has stray spaces or blank lines at the end. Open the file in a proper text editor, remove trailing whitespace, and ensure the file ends with a single newline.
  • Wrong indentation. Copying a block from one trait or dataset into another often carries the wrong indentation, which changes the structure YAML infers. Indent with spaces, never tabs, and keep sibling entries aligned to the same column.
  • Unquoted special characters. Values containing :, #, %, leading/trailing brackets, or a leading > or | can be misread. Wrap such values in quotes — for example units like "umol/m2/s" or a description containing a colon.

The creating a trait dictionary chapter has a dedicated section on writing valid traits.yml files and the formatting pitfalls to watch for, including a worked example of an indentation error and how to fix it. A useful habit is to validate a file by reading it back with yaml::read_yaml("config/traits.yml") — if it parses without error, the structure is sound.

27.7 Still stuck?

If none of the above resolves your problem, first update to the latest traits.build release in case the issue is a bug that has since been fixed, then follow the Getting help chapter: attribute the error, prepare a minimal reproducible example, and post to the relevant GitHub Discussions page.