Skip to main content

Bug fixes

Empty marked spans in Sanity Portable Text (issue #885) libs/sanity/portable_text.py wraps strong-marked span text in ** delimiters and em-marked text in _, unless the text touches edge whitespace, in which case it falls back to HTML. An empty span slipped between the two: _EDGE_WS_RE (^\s|\s$) needs a character to match, and "" has none, so the empty span took the delimiter path and emitted ****. A line consisting of **** is a CommonMark thematic break (§4.1), so a block whose only content was an empty bold run — or one sitting next to a span ending in a newline — rendered as <hr/> instead of nothing. The empty-span case now joins the existing HTML fallback (the same mechanism PR #775 added for edge whitespace, applied to the zero-length degenerate class) and emits an inert <strong></strong>. The em branch first looked safe: a lone __ is two delimiters — too short for a thematic break and unable to pair into emphasis. That holds only for a single span, though. _render_spans joins adjacent span output with no separator, so consecutive empty em spans join into ____ (or __ __ across a whitespace span), which §4.1 also accepts as a thematic break — the same <hr/> fallout via one more span. Found in review of this change; empty em spans now take the same fallback and emit an inert <em></em>. The trigger requires a hand-edited Portable Text document (the Studio editor serializes an empty selection as no span rather than an empty marked one), so this is defense-in-depth for the blog-archive converter, not an observed-data regression.
Last modified on October 2, 2026