How to escape percent in blog post title?

I would like to make a blog post with a % in the title. Using % alone gives me a panic with an invalid URL escape “%-d”. Replacing the % with %25 (the URL escape for %) makes the hugo code happy, but the title’s text includes the %25 instead of just the %. Help? I’m using the Icarus theme, if that matters.

If you just want it in the title, change the file name of the post and remove the percent from it. You can leave the % sign in the title in your mark down file.

$ mv content/post/fifteen-%-percent.md content/post/fifteen-percent.md

$ cat content/post/fifteen-percent.md 
---
date: 2016-12-14T22:39:14-06:00
title: fifteen 15% percent
---


$ hugo serve
Started building sites ...
Built site for language en:
...

Then access as http://localhost:1313/post/fifteen-percent/.

I can confirm the panic if the file name contains the percent sign. Kind of neat.

$ mv content/post/fifteen-percent.md content/post/fifteen-%-percent.md
$ hugo serve
Started building sites ...
ERROR: 2016/12/14 22:40:39 url.go:310: Failed to normalize URL string. Returning in = "post/fifteen-%-percent.html"
panic: parse post/fifteen-%-percent.html: invalid URL escape "%-p" [recovered]
	panic: parse post/fifteen-%-percent.html: invalid URL escape "%-p" [recovered]
	panic: parse post/fifteen-%-percent.html: invalid URL escape "%-p" [recovered]
	panic: parse post/fifteen-%-percent.html: invalid URL escape "%-p"

goroutine 166 [running]:
panic(0x65fd40, 0xc4205a84b0)
	/usr/local/Cellar/go/1.7.4/libexec/src/runtime/panic.go:500 +0x1a1
text/template.errRecover(0xc42028ba38)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:146 +0x18e
panic(0x65fd40, 0xc4205a84b0)
	/usr/local/Cellar/go/1.7.4/libexec/src/runtime/panic.go:458 +0x243
text/template.errRecover(0xc42028ad00)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:146 +0x18e
panic(0x65fd40, 0xc4205a84b0)
	/usr/local/Cellar/go/1.7.4/libexec/src/runtime/panic.go:458 +0x243
text/template.errRecover(0xc420289fc8)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:146 +0x18e
panic(0x65fd40, 0xc4205a84b0)
	/usr/local/Cellar/go/1.7.4/libexec/src/runtime/panic.go:458 +0x243
github.com/spf13/hugo/helpers.MakePermalink(0xc4202a09e0, 0x16, 0xc4201bf6a0, 0x1b, 0x1b)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/helpers/url.go:132 +0x4a9
github.com/spf13/hugo/hugolib.(*Page).createPermalink(0xc42040c000, 0x0, 0x0, 0xc4202890f0)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/hugolib/page.go:793 +0x5b0
github.com/spf13/hugo/hugolib.(*Page).getPermalink.func1()
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/hugolib/page.go:736 +0x36
sync.(*Once).Do(0xc420076d74, 0xc420289158)
	/usr/local/Cellar/go/1.7.4/libexec/src/sync/once.go:44 +0xdb
github.com/spf13/hugo/hugolib.(*Page).getPermalink(0xc42040c000, 0x20)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/hugolib/page.go:744 +0x53
github.com/spf13/hugo/hugolib.(*Page).Permalink(0xc42040c000, 0x0, 0x0)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/hugolib/page.go:870 +0x2b
reflect.Value.call(0x6e6f60, 0xc42040c000, 0x8613, 0x6e94b5, 0x4, 0xa5feb8, 0x0, 0x0, 0x242983, 0xa415a0, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/reflect/value.go:434 +0x5c8
reflect.Value.Call(0x6e6f60, 0xc42040c000, 0x8613, 0xa5feb8, 0x0, 0x0, 0xc420032450, 0x3d, 0x3d)
	/usr/local/Cellar/go/1.7.4/libexec/src/reflect/value.go:302 +0xa4
text/template.(*state).evalCall(0xc420289f48, 0x6e6f60, 0xc42040c000, 0x16, 0x6e6f60, 0xc42040c000, 0x8613, 0xa05ae0, 0xc420318bd0, 0xc4202bf616, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:658 +0x530
text/template.(*state).evalField(0xc420289f48, 0x6e6f60, 0xc42040c000, 0x16, 0xc4202bf616, 0x9, 0xa05ae0, 0xc420318bd0, 0xc420011fc0, 0x1, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:549 +0xdb6
text/template.(*state).evalFieldChain(0xc420289f48, 0x6e6f60, 0xc42040c000, 0x16, 0x6e6f60, 0xc42040c000, 0x16, 0xa05ae0, 0xc420318bd0, 0xc420011ea0, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:520 +0x22f
text/template.(*state).evalFieldNode(0xc420289f48, 0x6e6f60, 0xc42040c000, 0x16, 0xc420318bd0, 0xc420011fc0, 0x1, 0x1, 0x0, 0x0, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:484 +0x114
text/template.(*state).evalCommand(0xc420289f48, 0x6e6f60, 0xc42040c000, 0x16, 0xc420318ba0, 0x0, 0x0, 0x0, 0x531, 0xc4200a0b00, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:422 +0x866
text/template.(*state).evalPipeline(0xc420289f48, 0x6e6f60, 0xc42040c000, 0x16, 0xc42020d090, 0x0, 0x0, 0x98)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:400 +0xee
text/template.(*state).walk(0xc420289f48, 0x6e6f60, 0xc42040c000, 0x16, 0xa058a0, 0xc420318c00)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:226 +0x423
text/template.(*state).walk(0xc420289f48, 0x6e6f60, 0xc42040c000, 0x16, 0xa05c00, 0xc4201b9f80)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:234 +0x13d
text/template.(*Template).execute(0xc420317b40, 0x9fbd80, 0xc4201ffc00, 0x6e6f60, 0xc42040c000, 0x0, 0x0)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:189 +0x204
text/template.(*Template).Execute(0xc420317b40, 0x9fbd80, 0xc4201ffc00, 0x6e6f60, 0xc42040c000, 0x18, 0xc4201b9f20)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:175 +0x53
html/template.(*Template).Execute(0xc4201b9f20, 0x9fbd80, 0xc4201ffc00, 0x6e6f60, 0xc42040c000, 0xc42029a620, 0x17)
	/usr/local/Cellar/go/1.7.4/libexec/src/html/template/template.go:104 +0x78
github.com/spf13/hugo/tpl.executeTemplate(0x6e6f60, 0xc42040c000, 0x9fbd80, 0xc4201ffc00, 0xc42029a5c0, 0x2, 0x2)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/tpl/template.go:133 +0x101
github.com/spf13/hugo/tpl.ExecuteTemplateToHTML(0x6e6f60, 0xc42040c000, 0xc42029a5c0, 0x2, 0x2, 0x0, 0x0)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/tpl/template.go:150 +0xb5
github.com/spf13/hugo/tpl.partial(0xc4201d0a9d, 0x9, 0xc4204c2d60, 0x1, 0x1, 0x0, 0x0)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/tpl/template.go:120 +0x185
reflect.Value.call(0x63c340, 0x764fe8, 0x13, 0x6e94b5, 0x4, 0xc420547890, 0x2, 0x2, 0xc41ffd9e95, 0x765d13, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/reflect/value.go:434 +0x5c8
reflect.Value.Call(0x63c340, 0x764fe8, 0x13, 0xc420547890, 0x2, 0x2, 0xa05a80, 0xc4201def00, 0x6e6f60)
	/usr/local/Cellar/go/1.7.4/libexec/src/reflect/value.go:302 +0xa4
text/template.(*state).evalCall(0xc42028ac80, 0x6e6f60, 0xc42040c000, 0x16, 0x63c340, 0x764fe8, 0x13, 0xa05a20, 0xc420127ce0, 0xc4201d0a94, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:658 +0x530
text/template.(*state).evalFunction(0xc42028ac80, 0x6e6f60, 0xc42040c000, 0x16, 0xc420127d10, 0xa05a20, 0xc420127ce0, 0xc4202bb1c0, 0x3, 0x4, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:530 +0x195
text/template.(*state).evalCommand(0xc42028ac80, 0x6e6f60, 0xc42040c000, 0x16, 0xc420127ce0, 0x0, 0x0, 0x0, 0xc420408140, 0xc420408140, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:427 +0x6f7
text/template.(*state).evalPipeline(0xc42028ac80, 0x6e6f60, 0xc42040c000, 0x16, 0xc420425090, 0x0, 0x0, 0xc42028aca0)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:400 +0xee
text/template.(*state).walk(0xc42028ac80, 0x6e6f60, 0xc42040c000, 0x16, 0xa058a0, 0xc420127d40)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:226 +0x423
text/template.(*state).walk(0xc42028ac80, 0x6e6f60, 0xc42040c000, 0x16, 0xa05c00, 0xc420127c80)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:234 +0x13d
text/template.(*Template).execute(0xc4202bb140, 0x9fbd80, 0xc4201b1340, 0x6e6f60, 0xc42040c000, 0x0, 0x0)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:189 +0x204
text/template.(*Template).Execute(0xc4202bb140, 0x9fbd80, 0xc4201b1340, 0x6e6f60, 0xc42040c000, 0x1a, 0xc420127c20)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:175 +0x53
html/template.(*Template).Execute(0xc420127c20, 0x9fbd80, 0xc4201b1340, 0x6e6f60, 0xc42040c000, 0xc42058a540, 0x19)
	/usr/local/Cellar/go/1.7.4/libexec/src/html/template/template.go:104 +0x78
github.com/spf13/hugo/tpl.executeTemplate(0x6e6f60, 0xc42040c000, 0x9fbd80, 0xc4201b1340, 0xc42058a4e0, 0x2, 0x2)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/tpl/template.go:133 +0x101
github.com/spf13/hugo/tpl.ExecuteTemplateToHTML(0x6e6f60, 0xc42040c000, 0xc42058a4e0, 0x2, 0x2, 0x0, 0x0)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/tpl/template.go:150 +0xb5
github.com/spf13/hugo/tpl.partial(0xc4202d800c, 0xb, 0xc4205bc160, 0x1, 0x1, 0x0, 0x0)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/tpl/template.go:120 +0x185
reflect.Value.call(0x63c340, 0x764fe8, 0x13, 0x6e94b5, 0x4, 0xc4205670b0, 0x2, 0x2, 0xc41ffd21f5, 0x765d13, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/reflect/value.go:434 +0x5c8
reflect.Value.Call(0x63c340, 0x764fe8, 0x13, 0xc4205670b0, 0x2, 0x2, 0xa05a80, 0xc420129e20, 0x6e6f60)
	/usr/local/Cellar/go/1.7.4/libexec/src/reflect/value.go:302 +0xa4
text/template.(*state).evalCall(0xc42028b9b8, 0x6e6f60, 0xc42040c000, 0x16, 0x63c340, 0x764fe8, 0x13, 0xa05a20, 0xc4201eadb0, 0xc4202d8003, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:658 +0x530
text/template.(*state).evalFunction(0xc42028b9b8, 0x6e6f60, 0xc42040c000, 0x16, 0xc4201eade0, 0xa05a20, 0xc4201eadb0, 0xc42020bcc0, 0x3, 0x4, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:530 +0x195
text/template.(*state).evalCommand(0xc42028b9b8, 0x6e6f60, 0xc42040c000, 0x16, 0xc4201eadb0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:427 +0x6f7
text/template.(*state).evalPipeline(0xc42028b9b8, 0x6e6f60, 0xc42040c000, 0x16, 0xc4201fb810, 0xc420068000, 0xc4201ead80, 0xc4204019e8)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:400 +0xee
text/template.(*state).walk(0xc42028b9b8, 0x6e6f60, 0xc42040c000, 0x16, 0xa058a0, 0xc4201eae10)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:226 +0x423
text/template.(*state).walk(0xc42028b9b8, 0x6e6f60, 0xc42040c000, 0x16, 0xa05c00, 0xc4201ead80)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:234 +0x13d
text/template.(*Template).execute(0xc42020bc40, 0x9fbd80, 0xc4201b0d90, 0x6e6f60, 0xc42040c000, 0x0, 0x0)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:189 +0x204
text/template.(*Template).Execute(0xc42020bc40, 0x9fbd80, 0xc4201b0d90, 0x6e6f60, 0xc42040c000, 0xc4201e7c40, 0x1a)
	/usr/local/Cellar/go/1.7.4/libexec/src/text/template/exec.go:175 +0x53
html/template.(*Template).Execute(0xc4201ead20, 0x9fbd80, 0xc4201b0d90, 0x6e6f60, 0xc42040c000, 0x1a, 0xc4201ead20)
	/usr/local/Cellar/go/1.7.4/libexec/src/html/template/template.go:104 +0x78
github.com/spf13/hugo/hugolib.(*Site).renderThing(0xc4201e8000, 0x6e6f60, 0xc42040c000, 0xc4201e7c40, 0x1a, 0x9fbd80, 0xc4201b0d90, 0x0, 0xc42058e000)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/hugolib/site.go:1836 +0x96
github.com/spf13/hugo/hugolib.(*Site).renderForLayouts(0xc4201e8000, 0xc42058a460, 0x1e, 0x6e6f60, 0xc42040c000, 0x9fbd80, 0xc4201b0d90, 0xc4204dd800, 0xa, 0x10, ...)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/hugolib/site.go:1808 +0xcd
github.com/spf13/hugo/hugolib.(*Site).renderAndWritePage(0xc4201e8000, 0xc42058a460, 0x1e, 0xc42058a2e0, 0x1b, 0x6e6f60, 0xc42040c000, 0xc4204dd800, 0xa, 0x10, ...)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/hugolib/site.go:1715 +0x120
github.com/spf13/hugo/hugolib.pageRenderer(0xc4201e8000, 0xc42031bd40, 0xc42031bce0, 0xc42055be10)
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/hugolib/site_render.go:73 +0x3de
created by github.com/spf13/hugo/hugolib.(*Site).renderPages
	/Users/mdhender/Software/go/src/github.com/spf13/hugo/hugolib/site_render.go:46 +0x163

I don’t have it in the file name, just in the quoted title string within the file.

It’s that string I don’t know how to transform or escape.

Are you saying that you don’t get a panic if your title is “fifteen 15% percent” ?

Alas, just quoting it isn’t helping.

I have a filename with no percents, and the following string as my title:

title = "100% done!"

Here’s my traceback:

panic: parse 100%-done: invalid URL escape "%-d"

goroutine 1 [running]:
panic(0xbc71e0, 0xc8203ddbc0)
	/usr/lib/go-1.6/src/runtime/panic.go:481 +0x3e6
github.com/spf13/hugo/helpers.URLize(0xc8203a3240, 0xa, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/helpers/url.go:111 +0xa4
github.com/spf13/hugo/hugolib.pageToPermalinkTitle(0xc8201e9800, 0xc8200ac7b4, 0x4, 0x0, 0x0, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/permalinks.go:155 +0x52
github.com/spf13/hugo/hugolib.pageToPermalinkSlugElseTitle(0xc8201e9800, 0xc8200ac7b4, 0x4, 0x0, 0x0, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/permalinks.go:178 +0x2d3
github.com/spf13/hugo/hugolib.pathPattern.Expand(0xc8200ac7a0, 0x18, 0xc8201e9800, 0x0, 0x0, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/permalinks.go:114 +0x64c
github.com/spf13/hugo/hugolib.(*Page).permalink(0xc8201e9800, 0x30, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/page.go:437 +0x37e
github.com/spf13/hugo/hugolib.(*Page).RelPermalink(0xc8201e9800, 0x0, 0x0, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/page.go:498 +0x54
github.com/spf13/hugo/hugolib.(*Page).Menus.func1()
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/page.go:742 +0x130
sync.(*Once).Do(0xc8201e99b8, 0xc8200fd0e0)
	/usr/lib/go-1.6/src/sync/once.go:44 +0xe4
github.com/spf13/hugo/hugolib.(*Page).Menus(0xc8201e9800, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/page.go:787 +0x48
github.com/spf13/hugo/hugolib.(*Site).assembleMenus(0xc820346000)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/site.go:1209 +0x13c6
github.com/spf13/hugo/hugolib.(*Site).buildSiteMeta(0xc820346000, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/site.go:1111 +0x33
github.com/spf13/hugo/hugolib.(*Site).Process(0xc820346000, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/site.go:694 +0x15b
github.com/spf13/hugo/hugolib.(*Site).Build(0xc820346000, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/hugolib/site.go:376 +0x47
github.com/spf13/hugo/commands.buildSite(0xc8200fdb17, 0x1, 0x1, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/commands/hugo.go:688 +0x1b8
github.com/spf13/hugo/commands.build(0x0, 0x0, 0x0, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/commands/hugo.go:525 +0x3be
github.com/spf13/hugo/commands.glob.func7(0x10875e0, 0xc8200ac3c0, 0x0, 0x2, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/commands/hugo.go:119 +0xdd
github.com/spf13/cobra.(*Command).execute(0x10875e0, 0xc82000a160, 0x2, 0x2, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/cobra/command.go:568 +0x668
github.com/spf13/cobra.(*Command).ExecuteC(0x10875e0, 0x10875e0, 0x0, 0x0)
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/cobra/command.go:658 +0x55c
github.com/spf13/hugo/commands.Execute()
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/commands/hugo.go:168 +0x77
main.main()
	/build/hugo-0.16/obj-x86_64-linux-gnu/src/github.com/spf13/hugo/main.go:26 +0x28

If I replace the percent sign in the quoted title string with the URL-encoded percent – %25 – I get 100%25 in my post title and in the sidebar. What I’d like is a URL that’s something like 100-done and a title for the post and sidebar of “100% done!”.

Yes, that is working with 0.18.

Can you do something to help me verify this?

Give me the name of the file as you have it, then cat out the front matter of the file? Or point me to a repo on GitHub that has this.

Thanks,
Mike

Mattias from the IRC channel made a great suggestion that I try manually setting the slug. After doing that, I am no longer getting errors and the pages look as expected.

The version of Hugo I am running is 0.17. I will be updating to 0.18 soon.

Thank you for your help!

1 Like

Now that Hugo 0.18 has been released, I gave it a try without the slug setting. My new slug is “100-done” which is exactly what I would expect based on your comments. Thank you again for all your help!

1 Like