squidfunk/mkdocs-material · error · ValidationError

Expected list, but received: {tags}

Error message

Expected list, but received: {tags}

What it means

Within a deserialized mapping, `_mapping_from_json` requires the `tags` field to be a list and then each tag to be a string; a non-list `tags` value (or, per the following loop, non-string elements) raises this ValidationError, ensuring tag data is uniformly typed.

Source

Thrown at src/plugins/tags/structure/mapping/storage/__init__.py:161

def _mapping_from_json(data: object) -> Mapping:
    """
    Return a mapping from a serialized representation.

    Arguments:
        data: Serialized representation.

    Returns:
        The mapping.
    """
    if not isinstance(data, dict):
        raise ValidationError(
            f"Expected dictionary, but received: {data}"
        )

    # Ensure tags are iterable
    tags = data.get("tags")
    if not isinstance(tags, list):
        raise ValidationError(
            f"Expected list, but received: {tags}"
        )

    # Ensure tags are valid
    for tag in tags:
        if not isinstance(tag, str):
            raise ValidationError(
                f"Expected string, but received: {tag}"
            )

    # Create and return mapping
    return Mapping(
        _mapping_item_from_json(data.get("item")),
        tags = [Tag(tag) for tag in tags]
    )

def _mapping_item_from_json(data: object) -> Link:
    """

View on GitHub (pinned to e2136532f4)

Solutions

  1. Change `tags` to an array of strings: `{"tags": ["tag-a"]}`.
  2. Convert any single tag string into a one-element list.
  3. Regenerate the mappings file with the plugin's export tooling to get canonical shapes.
  4. Validate with a guard (`isinstance(tags, list) and all(isinstance(t, str) for t in tags)`) before loading.

Example fix

// before
{"tags": "tag-a"}

// after
{"tags": ["tag-a"]}
Defensive patterns

Strategy: type-guard

Validate before calling

import json
data = json.load(open(path))
for i, entry in enumerate(data.get('mappings', [])):
    tags = entry.get('tags')
    assert isinstance(tags, list) and all(isinstance(t, str) for t in tags), f"mappings[{i}].tags must be a list of strings"

Type guard

def has_valid_tags(entry):
    tags = entry.get('tags') if isinstance(entry, dict) else None
    return isinstance(tags, list) and all(isinstance(t, str) for t in tags)

Try / catch

try:
    yield from storage.load(path)
except ValidationError as e:
    log.error(f"Invalid tags field in {path}: {e}")
    raise SystemExit(1)

Prevention

When it happens

Trigger: A mapping entry where `tags` is a string (`"tag-a"`), an object, or null instead of an array of strings; entries with numeric or nested-structure tag elements.

Common situations: Hand-authored exports using a single tag string; JSON produced by scripts that forgot to wrap a tag in a list; front matter styles where tags were a scalar and exported as-is.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of squidfunk/mkdocs-material@e2136532f4 (2026-08-29). Data as JSON: /api/errors/f4fbd897a6fc895d. Report an issue: GitHub.