squidfunk/mkdocs-material · error · ValidationError
Expected string, but received: {url}
Error message
Expected string, but received: {url} What it means
After confirming the mapping item is a dictionary, _mapping_item_from_json requires its 'url' field to be a string so it can construct an mkdocs Link. This ValidationError is thrown when 'url' is missing (None) or of another JSON type (number, object, array, bool).
Source
Thrown at src/plugins/tags/structure/mapping/storage/__init__.py:199
When loading a mapping, we must always return a link, as the sources of
pages might not be available because we're building another project.
Arguments:
data: Serialized representation.
Returns:
The link.
"""
if not isinstance(data, dict):
raise ValidationError(
f"Expected dictionary, but received: {data}"
)
# Ensure item has URL
url = data.get("url")
if not isinstance(url, str):
raise ValidationError(
f"Expected string, but received: {url}"
)
# Ensure item has title
title = data.get("title")
if not isinstance(title, str):
raise ValidationError(
f"Expected string, but received: {title}"
)
# Create and return item
return Link(title, url)
View on GitHub (pinned to e2136532f4)
Solutions
- Add or fix the 'url' field inside each mapping's item so it is a plain string URL path, e.g. "url": "guide/setup/"
- Regenerate the mapping file by rebuilding the source project with MappingStorage.save()
- Search the JSON for items missing "url" (e.g. with jq: .mappings[] | select(.item.url | type != "string"))
- If a script writes the file, make it serialize item.url as str(item.url)
Example fix
// before
{"item": {"title": "Setup"}, "tags": ["setup"]}
// after
{"item": {"url": "setup/", "title": "Setup"}, "tags": ["setup"]} Defensive patterns
Strategy: validation
Validate before calling
import json
with open(path) as f:
data = json.load(f)
for m in data.get("mappings", []):
url = (m.get("item") or {}).get("url")
if not isinstance(url, str):
raise ValueError(f"entry {m!r}: item.url must be a string") Type guard
def item_has_url(entry: dict) -> bool:
item = entry.get("item")
return isinstance(item, dict) and isinstance(item.get("url"), str) Try / catch
from mkdocs.config.base import ValidationError
try:
mappings = list(storage.load(path))
except ValidationError as err:
log.error("Item missing/invalid url in %s: %s", path, err)
mappings = [] Prevention
- Ensure page/item URL is serialized with str(url), never None
- Check with jq before use: .mappings[] | select(.item.url == null)
- Restore missing URLs from the source project's mkdocs.yml nav
- Keep the file generated by the plugin so fields are never dropped
When it happens
Trigger: MappingStorage.load(path) reads a mapping entry where data['item']['url'] is absent, null, or e.g. a nested object/array instead of a string like "guide/setup/".
Common situations: Hand-edited mapping files where the url key was renamed or deleted; a custom exporter that emitted url as a list of path segments; partially written JSON from a crashed build.
Related errors
- Expected string, but received: {title}
- Error reading filter configuration in '{key}': {e}
- Relative path processor not registered
- Unknown shortcode: {type}
- Unknown type: {type}
AI-assisted analysis of squidfunk/mkdocs-material@e2136532f4 (2026-08-29).
Data as JSON: /api/errors/796946116d2f827f.
Report an issue: GitHub.