Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### 🐛 Bug Fixes

- 🐛 Don't crash when a YAML date/datetime is nested in front matter by <gh-user:00200200>

## 5.1.0 - 2026-05-13

### ✨ New Features
Expand Down
7 changes: 5 additions & 2 deletions myst_parser/mdit_to_docutils/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1387,8 +1387,11 @@ def dict_to_fm_field_list(
)
continue
try:
value = json.dumps(value)
except (ValueError, RecursionError):
# Nested date/datetime values (allowed at top level) are
# not JSON-serializable; stringify them like scalars.
# TypeError also covers non-string keys such as a YAML date.
value = json.dumps(value, default=str)
except (ValueError, TypeError, RecursionError):
# e.g. a self-referential structure via a YAML alias
self.create_warning(
f"Front matter field {key!r} could not be serialized",
Expand Down
42 changes: 42 additions & 0 deletions tests/test_docutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,48 @@ def test_topmatter_deeply_nested_yaml_warns():
assert "content" in doctree.pformat()


@pytest.mark.parametrize(
"yaml_block",
[
"review:\n date: 2026-08-03", # date nested in a mapping
"reviewed: [2026-08-03]", # date nested in a sequence
"review:\n at: 2026-08-03T12:00:00", # datetime nested in a mapping
],
)
def test_topmatter_nested_dates_do_not_crash(yaml_block):
"""YAML dates nested in dicts/lists must not abort the build.

Top-level ``date``/``datetime`` scalars skip JSON encoding, but a nested
date used to reach ``json.dumps`` and raise ``TypeError`` (#1203).
"""
stream = io.StringIO()
doctree = publish_doctree(
source=f"---\n{yaml_block}\n---\n\ncontent\n",
parser=Parser(),
settings_overrides={"warning_stream": stream},
)
assert "content" in doctree.pformat()
assert "2026-08-03" in doctree.astext()
assert "[myst.topmatter]" not in stream.getvalue()


def test_topmatter_date_mapping_key_warns():
"""A YAML date used as a mapping *key* cannot be JSON-serialized.

``json.dumps(..., default=str)`` does not convert keys, so this still
cannot be rendered as a field, but it must warn rather than crash.
"""
stream = io.StringIO()
doctree = publish_doctree(
source="---\nreview:\n 2026-08-03: done\n---\n\ncontent\n",
parser=Parser(),
settings_overrides={"warning_stream": stream},
)
assert "content" in doctree.pformat()
assert "could not be serialized" in stream.getvalue()
assert "[myst.topmatter]" in stream.getvalue()


def test_topmatter_alias_expansion_bomb_warns():
"""A YAML alias-expansion ("billion laughs") bomb warns, not hangs.

Expand Down