Template fails at .Fragments.Headings

After updating Hugo from v0.120.1 to v0.124.1 my custom toc.html partial stopped working. Here is the issue the logs present me:

execute of template failed at <.Fragments.Headings>: nil pointer evaluating *tableofcontents.Fragments.Headings

A did some debug. By checking the .Fragments results, editing the partial to only contain the following line:
<pre>{{ .Fragments | jsonify (dict "indent" " ") }}</pre>

I get something expected that resembles this:

{
  "Headings": [
    {
      "ID": "",
      "Level": 0,
      "Title": "",
      "Headings": [
        {
          "ID": "_introduction",
          "Level": 0,
          "Title": "Introduction",
          "Headings": null
        },
        {

// ...more stuff like that

}

But I can easily trigger the error again, by just adding the .Headings portion to that debug line. Meaning that <pre>{{ .Fragments.Headings | jsonify (dict "indent" " ") }}</pre> triggers the same error I get in my regular toc.html partial.

Is the problem that first “empty” level? Could it be due to the fact that I skip the Level 1 heading in my markup because I use the frontmatter title to render that in my final page?

Any pointers would be highly appreciated. I’d be interested in knowing whether something relative to this changed recently, I did browse the changelog, but couldn’t find anything specific.

Can you help me to reproduce this by providing:

  1. The markdown for the page in question
  2. Your code

I wrote a TOC partial a long time ago that walks .Page.Fragments.Headings. I’ve thoroughly tested this with v0.124.1 without error, but perhaps I’m missing a test case.

I will put something together later today as I’m still at work.

I can already partially answer 1. since I’m not using Markdown, but rather AsciiDoc as markup.

I’m aware it doesn’t offer the same integration, but following the website documentation I was able to get the TOC running nonetheless.

With AsciiDoc content and rendering I am still unable to reproduce the problem with v0.124.1, so there must be different about your content or setup.

content/posts/post-1.adoc

---
title: Post 1
---

:toc:

## Section 1

This is paragraph 1.

### Section 1.1

This is paragraph 2.

### Section 1.2

This is paragraph 3.

layouts/_default/single.html

{{ define "main" }}
  <h1>{{ .Title }}</h1>

  <h3>Hugo's .Page.TableOfContents method</h3>
  {{ .TableOfContents }}

  <h3>jsonify .Fragments.Headings</h3>
  <pre>{{ .Fragments.Headings | jsonify (dict "indent" " ") }}</pre>

  {{ .Content }}
{{ end }}

test site

git clone --single-branch -b hugo-forum-topic-48963 https://github.com/jmooring/hugo-testing hugo-forum-topic-48963
cd hugo-forum-topic-48963
hugo server

OK, I can reproduce it if I have a page without TOC enabled (i.e., does not contain the AsciiDoc :toc: document attribute).

v0.124.1

.Fragments is nil when TOC is disabled (hence the error):

v0.122.0

.Fragments is not nil when TOC is disabled:

{
  "Headings": [],
  "Identifiers": null,
  "HeadingsMap": {}
}

What you should do

Code defensively, but you should be doing that regardless of version.

{{ with .Fragments }}
  <pre>{{ .Headings | jsonify (dict "indent" " ") }}</pre>
{{ end }}
1 Like

Thanks a lot for your help troubleshooting this, and offering a solution.

You’re welcome. One other thing to note: when using .Fragments with AsciiDoc content the Level will always be zero.

That’s right, thanks for mentioning that. This is how I solved, just in case anybody stumbles upon this. Most likely there are better ways to overcome that limitation, but this worked well for my needs so far.

{{- with .Fragments -}}
    <nav>
      <div>
        {{- with . -}}
          <div class="toc-title">Contents</div>
          {{- range .Headings -}}
            <ul class="toc">
              {{- with .Headings -}}{{ template "toc-subheading" (dict "headings" . "level" 0) }}{{- end -}}
            </ul>
          {{- end -}}
        {{- end -}}
      </div>
    </nav>
{{ end -}}

{{- define "toc-subheading" -}}
  {{- $headings := .headings -}}
  {{- $level := .level -}}
  {{- $padding := (mul $level 4) -}}
  {{- $class := cond (gt $level 0) (printf "padding-left-%d" (add $padding 2) ) "padding-left-3" -}}
  {{- range $headings }}
    {{- if .Title }}
      <li class="toc-item">
        <a class="toc-item-link {{ $class }}" href="#{{ anchorize .ID }}">
          {{- .Title | safeHTML | plainify | htmlUnescape -}}
        </a>
      </li>
    {{- end -}}
    {{- with .Headings -}}
      {{ template "toc-subheading" (dict "headings" . "level" (add $level 1)) }}
    {{- end -}}
  {{- end -}}
{{- end -}}

Of course the amount of padding is a designer’s decision, but I left it in there as a proof of concept.

https://github.com/gohugoio/hugo/issues/12291 This (the level 0 problem) will be fixed shortly.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.