Solving Inconsistent Blank Blog Homepage
For the past few months, when I publish a post to this blog there’s been a small chance that the homepage would be a blank empty page but bizarrely all the other pages seemed to work just fine. After a while of banging my head against the problem I managed to figure out what was going on. This post explores how I figured it out, what the problem was, and how I eventually solved it.
What is Hugo?
Let’s start with a quick primer on how this blog works. It’s built using a static site generator called Hugo. Being a static site generator the site doesn’t run on a server, instead I run the hugo
command once and it transforms the content I’ve written into static HTML, JavaScript, and CSS which I can publish.
Is it a Bug?
The first thing I tried was to switch to a newer version of Hugo to see if it’d fix the issue. I upgraded to 0.123.8, but that didn’t improve anything - in fact it made things worse. Whilst my initial issue was intermittent, version 0.123.8 always built a blank homepage. I’d found a way to reproduce the issue!
The next step was to see if this was a bug in Hugo itself. I searched the GitHub issues and I couldn’t find anyone else running into the same issue. Trying to reproduce the build on another Hugo site of mine didn’t work either. At this point, it seemed likely that this was an issue with my blog and not with Hugo.
Getting Log Output
I decided to take the approach of seeing if I could make the build command produce more output to chase down the issue. Reading the docs (as always!) helped me piece together a command and this is what I ended up with:
hugo --printPathWarnings --printUnusedTemplates --logLevel debug --cleanDestinationDir --ignoreCache
.
This command:
- makes sure every build was fresh by ignoring the cache and cleaning the build directory
- prints out warnings for when a destination file was written to multiple times
- lists any theme layout files that were not being used to build the site
- prints out the operations in the highest level of verbosity
After running the command, this was the initial output I got.
INFO static: removing all files from destination that don't exist in static dirs
INFO static: syncing static files to / duration 56.116625ms
INFO build: step process substep collect files 148 files_total 148 duration 81.55375ms
INFO build: step process duration 81.619875ms
INFO build: step assemble duration 2.2515ms
INFO build: step render substep pages site en outputFormat html duration 74.609458ms
INFO build: step render substep pages site en outputFormat meilisearch duration 203.75µs
INFO build: step render substep pages site en outputFormat rss duration 698.208µs
INFO build: step render pages 131 content 125 duration 75.72825ms
WARN Duplicate target paths: post/index.html (19)
WARN Template _default/list.meilisearch.json is unused, source file /Users/arran/projects/blog/layouts/_default/list.meilisearch.json
WARN Template _default/single.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/_default/single.html
WARN Template index.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/index.html
WARN Template partials/disqus.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/partials/disqus.html
WARN Template partials/loading.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/partials/loading.html
WARN Template partials/pager.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/partials/pager.html
WARN Template partials/progress.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/partials/progress.html
WARN Template partials/stripe.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/partials/stripe.html
INFO build: step postProcess duration 11.042µs
INFO build: duration 160.006459ms
A few lines stuck out to me.
WARN Template index.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/index.html`
WARN Template _default/single.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/_default/single.html`
WARN Duplicate target paths: post/index.html (19)`
The Homepage Bug
Template index.html is unused
indicated that the file that was meant to be defining the output of the homepage wasn’t being used as a part of the build process. I assumed perhaps some other layout file was being used instead. However, Hugo defines an order of precedence for looking up the layout file for the homepage and /layouts/index.html
is the top of the list.
For some reason Hugo was being configured not to build the homepage. I figured the best place to start investigating that was in Hugo’s configuration file (config.toml
). To isolate the configuration that might be causing this behaviour I commented out all the configuration and then slowly started uncommenting small sections at a time to identify the culprit. At the end of that process I isolated the issue to two lines of config related to taxonomies.
[taxonomies]
# See: https://gohugo.io/content-management/taxonomies/#default-taxonomies
disableKinds = ['taxonomy', 'term']
By default, when you create a taxonomy Hugo generates two types of page related to it:
- a page for the taxonomy itself, which lists all the terms in the taxonomy
- a page for each of terms, which lists all the content related to that term
Hugo comes with two taxonomies out of the box: tags and categories. I don’t use categories at all, and whilst I do use tags to group my posts I didn’t want Hugo to generate pages specifically for them. By setting the disableKinds
configuration I was configuring Hugo to not generate any term or taxonomy pages, and therefore not generate pages for tags or categories. Or at least theoretically that was what I was doing.
However, after rereading the line of configuration more closely I noticed I actually made a mistake copy-pasting the configuration. Instead of being nested under taxonomies it should’ve been at the top level instead. By moving it to the right place, my homepage immediately started generating correctly again. Hurrah!
However - not everything was quite solved. I still didn’t understand why that piece of rogue configuration was causing problems or what was causing the warning about “duplicate target paths”.
After a bit of experimentation, and some skimming of Hugo’s GitHub, I think I can explain some pieces of the puzzle. The taxonomies section of config controls which taxonomies exist and so my broken config was likely being interpreted as a definition of a taxonomy of name disableKinds! This had the effect of removing the default tag and categories taxonomies - which made it appear like my configuration was working. I wasn’t removing the pages, I was stopping the taxonomies from existing in the first place.
As to why my misplaced configuration causes Hugo to fail to find the correct template, that’s more of a mystery. Hugo also appears to have a few regressions related to taxonomies and template lookup (#12146, #12150, and #12193). By setting any key-pair value in the taxonomies
section of config I’m able to reproducibly break homepage template lookup. I’ve opened an issue #12317 on GitHub, and whilst the maintainer who commented back didn’t seem very thrilled to receive the bug report - it is marked as a bug.
[taxonomies]
willfailtoproducehomepage = []
Duplicate post/index.html
As for my duplicate target paths, that was a tricky issue of its own. By default, Hugo generates a section page for each type of content which lists all of that type of content. In my case, /posts/index.html
should be a list of all posts. However, the warning output by Hugo (WARN Template index.html is unused, source file /Users/arran/projects/blog/themes/hemingpress/layouts/index.html
) was indicating this path was being written to 19 times. That was clearly an error. I didn’t need /posts/index.html
, so I could simply add section
to my disableKinds
configuration to stop generating the page, but I was concerned something more fundamentally wrong was going on.
I was curious why this was only occurring 19 times - I have 248 posts and so that meant only a subset were causing the problem. From that, I was able to deduce it was likely an issue with some specific posts’ Markdown frontmatter - the part used by Hugo to define configuration for that page. If some posts had frontmatter mistakenly configured to /
then Hugo would write a index.html
file to /posts
causing the issue.
I took the same approach I did with the configuration file - divide and conquer! I removed every post from the folder, and added back in a few at a time until when I built the site the error resurfaced.
After opening the first of the offending files the issue became quickly apparent, the post’s alias was set to an empty string. The file had its alias set by a template when it was initially created, and I hadn’t deleted the field or changed it when I published it. A quick find and replace removed all the offenders and the site built for the first time without warnings!
Preventing Issues in the Future
Part of the problem in this case was knowing there was a problem to begin with. Hugo silently warned me about these issues but built the site anyway.
I’ve updated how I build the site to be a little safer using the following script. It builds the site twice. The first time it builds it ensuring that failed translations (not that I use those!) and pages that failed to build are still output. Then it greps over the input returning an exit code > 0 if it detects any indicators building may have silently failed. The second time it builds with warnings enabled, and returns an exit code > 0 if any warning is detected.
set -ex
ensures that the script itself will exit and fail if any of the prior steps return a non-zero exit code.
set -ex
# See: https://gohugo.io/troubleshooting/audit/
HUGO_MINIFY_TDEWOLFF_HTML_KEEPCOMMENTS=true HUGO_ENABLEMISSINGTRANSLATIONPLACEHOLDERS=true hugo
# -v returns exit code 0 when no matches.
grep -vinorE "<\!-- raw HTML omitted -->|ZgotmplZ|\[i18n\]|\(<nil>\)|(<nil>)|hahahugo" public/
hugo --printPathWarnings --logLevel debug --cleanDestinationDir --ignoreCache --panicOnWarning