ultralytics/ultralytics · critical · SyntaxError
{dataset} key missing ❌. either 'names' or 'nc' are require
Error message
{dataset} key missing ❌.
either 'names' or 'nc' are required in all data YAMLs. What it means
Raised by check_det_dataset when the data YAML declares neither 'names' nor 'nc'. At least one is mandatory: names (list or dict of class names) or nc (integer class count, from which placeholder names class_0..class_{nc-1} are generated). A bare 'names:' that parses to None counts as missing, which is why the check uses `is None` rather than key membership.
Source
Thrown at ultralytics/data/utils.py:522
# Read YAML
data = YAML.load(file, append_filename=True) # dictionary
# Checks
for k in "train", "val":
if k not in data:
if k != "val" or "validation" not in data:
raise SyntaxError(
emojis(f"{dataset} '{k}:' key missing ❌.\n'train' and 'val' are required in all data YAMLs.")
)
LOGGER.warning("renaming data YAML 'validation' key to 'val' to match YOLO format.")
data["val"] = data.pop("validation") # replace 'validation' key with 'val' key
if split and not data.get(split):
raise FileNotFoundError(f"{dataset} '{split}:' images not found ❌")
# `names` compared to None, not membership: a bare `names:` parses to None and len(None) below
# raises. `nc` stays membership so a valueless `nc:` still reaches its "must be an integer" error.
if data.get("names") is None and "nc" not in data:
raise SyntaxError(emojis(f"{dataset} key missing ❌.\n either 'names' or 'nc' are required in all data YAMLs."))
if "nc" in data and not isinstance(data["nc"], int):
try:
nc = float(data["nc"]) # accept integer-like values, e.g. '10' or 10.0, but not 1.9 or placeholders
if nc != int(nc):
raise ValueError
data["nc"] = int(nc)
except (TypeError, ValueError):
raise SyntaxError(emojis(f"{dataset} 'nc: {data['nc']}' must be an integer ❌."))
if data.get("names") is not None and data.get("nc") is not None and len(data["names"]) != data["nc"]:
raise SyntaxError(emojis(f"{dataset} 'names' length {len(data['names'])} and 'nc: {data['nc']}' must match."))
if data.get("names") is None:
data["names"] = [f"class_{i}" for i in range(data["nc"])]
else:
data["nc"] = len(data["names"])
data["names"] = check_class_names(data["names"])
data["channels"] = data.get("channels", 3) # get image channels, default to 3
View on GitHub (pinned to 0449ea011c)
Solutions
- Add a names mapping, e.g. 'names:\n 0: person\n 1: car'
- Or add 'nc: <int>' to get auto-generated placeholder names class_0..class_{nc-1}
- If names was already written, verify it is not empty/null in the parsed YAML (print(YAML.load(...)))
- Check indentation — entries under names: must be indented one level
Example fix
# before (my.yaml) train: images/train val: images/val # after train: images/train val: images/val names: 0: cat 1: dog
Defensive patterns
Strategy: validation
Validate before calling
from ultralytics.utils import YAML
def validate_classes(path: str) -> dict:
data = YAML.load(path)
if data.get('names') is None and 'nc' not in data:
raise ValueError(f"{path} must define either 'names:' or 'nc:'")
return data Prevention
- Prefer explicit 'names:' dicts over bare nc so classes are self-documenting
- Never leave 'names:' or 'nc:' empty — delete the line instead
- Generate YAMLs from your labels dir with a small script so names are never forgotten
When it happens
Trigger: train(data='my.yaml') where my.yaml has train:/val: but no names/nc; a YAML with 'names:' written but no classes under it (parses to None); commenting out names while leaving nc out.
Common situations: Truncated or template YAMLs; users who assume class names come from the model checkpoint instead of the dataset YAML; indentation bugs that orphan the names entries.
Related errors
- {dataset} 'nc: {data['nc']}' must be an integer ❌.
- {dataset} 'names' length {len(data['names'])} and 'nc: {data
- {dataset} '{k}:' key missing ❌. 'train' and 'val' are requir
- {dataset} '{split}:' images not found ❌
- 0-class dataset, at least one class name is required in your
AI-assisted analysis of ultralytics/ultralytics@0449ea011c (2026-08-15).
Data as JSON: /api/errors/68d45b6790867853.
Report an issue: GitHub.