Solving mysterious unrendered markdown headings

| 3 min read

I finally spent some time getting to the bottom of why some headings in my markdown content weren't getting rendered properly.

I've noticed over the years that occasionally the rendered version of my markdown content, in particular on GitHub (which is where most of my markdown content ends up), sometimes contains unrendered headings. Here's an example:

Rendered markdown showing an unrendered heading - on GitHub

The second level 2 heading "Another heading level 2" remains unrendered, even though everything looks fine. Why? This has bugged me for a while, but not so much as to make me stop and work out why it was happening. When it happened, I'd just go into the markdown source, rewrite the heading line, and all was fine.

Today I finally stopped to spend a bit of time to look into it. Turns out it's quite simple and obvious now I know what was causing it.

The basic syntax for headings involves one or more hashes (depending on the heading level needed) followed by the heading text. There's a space that should separate the hashes and the heading text. Here's an example:

## Heading level 2

What's causing that heading above not to be rendered properly? Well, it's the space. To you and me there is indeed a space between ## and Another heading level 2.

But it's the wrong type of space.

Checking first that it's not something weird going on with the markdown renderer on GitHub, let's try a different rendering, in the terminal, with the excellent glow tool:

Rendered markdown showing an unrendered heading - using glow

Same issue.

So let's dig in a little deeper, and look at the source.

First, let's look at the first level 2 heading, which has been rendered correctly:

# ~/Projects/gh/github.com/qmacro-org/test (main=)
; grep 'Heading level 2' README.md | od -t x1 -c
0000000    23  23  20  48  65  61  64  69  6e  67  20  6c  65  76  65  6c
           #   #       H   e   a   d   i   n   g       l   e   v   e   l
0000020    20  32  0a
               2  \n
0000023
# ~/Projects/gh/github.com/qmacro-org/test (main=)
;

Seems OK, and yes, there's the space, hex value 20, following the two hashes (hex values 23).

Now let's look at the second level 2 heading, which has not been correctly rendered;

# ~/Projects/gh/github.com/qmacro-org/test (main=)
; grep 'Another heading level 2' README.md | od -t x1 -c
0000000    23  23  c2  a0  41  6e  6f  74  68  65  72  20  68  65  61  64
           #   # 302 240   A   n   o   t   h   e   r       h   e   a   d
0000020    69  6e  67  20  6c  65  76  65  6c  20  32  0a
           i   n   g       l   e   v   e   l       2  \n
0000034
# ~/Projects/gh/github.com/qmacro-org/test (main=)
;

What the heck is that following the two hex 23 hash characters?

0000000    23  23  c2  a0
           #   # 302 240

Turns out it's a non-breaking space character. And its UTF-8 encoding, which is what the markdown file has, is c2 a0.

So this second level 2 heading cannot be rendered as such, as the markdown cannot be recognised. Makes sense!

But where are these non-breaking space coming from? How do they get there?

Well, my daily driver during the working week is a macOS device, where it's notoriously more difficult that it should be to type a # character. One has to use Option-3 (or Alt-3) to get it. And it turns out that after holding down Option to hit 3 a couple of times to introduce the ## for a level 2 heading, my thumb is sometimes still on the Option key when I hit space.

And guess what - Option-space is how you type a non-breaking space on macOS!

So basically it's me that's been causing this issue - by inadvertently inserting not a space but a non-breaking space after the # characters introducing markdown headings.