Repository: gohugoio/hugo (language: go)

Issue:
panic: markup template must have at least 2 parts
To recreate:

```text
git clone https://github.com/HugoBlox/theme-research-group
cd theme-research-group/
hugo
```

I just found this and have not dug into the details.

```text
hugo v0.146.0-DEV-653f1c1d462332bccf303a5040e5cd99c89a4378+extended+withdeploy linux/amd64
```


Discussion / hints:
Well, the error message is not great, but they have a `/layouts/_markup/sitemap.xml` file that trips the parsing logic. I'm not sure what to do, I suspect that a warning is the best medicine (for now anyway).

Fix patch:
diff --git a/hugolib/site.go b/hugolib/site.go
index ebf06eee392..728b036d20e 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -396,6 +396,7 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
 			templateStore, err := tplimpl.NewStore(
 				tplimpl.StoreOptions{
 					Fs:                     s.BaseFs.Layouts.Fs,
+					Log:                    s.Log,
 					DefaultContentLanguage: s.Conf.DefaultContentLanguage(),
 					Watching:               s.Conf.Watching(),
 					PathParser:             s.Conf.PathParser(),


Test patch:
diff --git a/tpl/tplimpl/templatestore.go b/tpl/tplimpl/templatestore.go
index f6a119e9b5a..df4ea649f83 100644
--- a/tpl/tplimpl/templatestore.go
+++ b/tpl/tplimpl/templatestore.go
@@ -20,6 +20,7 @@ import (
 	"time"
 
 	"github.com/gohugoio/hugo/common/herrors"
+	"github.com/gohugoio/hugo/common/loggers"
 	"github.com/gohugoio/hugo/common/maps"
 	"github.com/gohugoio/hugo/common/paths"
 	"github.com/gohugoio/hugo/helpers"
@@ -156,6 +157,9 @@ type StoreOptions struct {
 	// The filesystem to use.
 	Fs afero.Fs
 
+	// The logger to use.
+	Log loggers.Logger
+
 	// The path parser to use.
 	PathParser *paths.PathParser
 
@@ -986,7 +990,10 @@ func (s *TemplateStore) setTemplateByPath(p string, ti *TemplInfo) {
 }
 
 func (s *TemplateStore) insertShortcode(pi *paths.Path, fi hugofs.FileMetaInfo, replace bool, tree doctree.Tree[map[string]map[TemplateDescriptor]*TemplInfo]) (*TemplInfo, error) {
-	k1, k2, _, d := s.toKeyCategoryAndDescriptor(pi)
+	k1, k2, _, d, err := s.toKeyCategoryAndDescriptor(pi)
+	if err != nil {
+		return nil, err
+	}
 	m := tree.Get(k1)
 	if m == nil {
 		m = make(map[string]map[TemplateDescriptor]*TemplInfo)
@@ -1027,7 +1034,18 @@ func (s *TemplateStore) insertShortcode(pi *paths.Path, fi hugofs.FileMetaInfo,
 }
 
 func (s *TemplateStore) insertTemplate(pi *paths.Path, fi hugofs.FileMetaInfo, replace bool, tree doctree.Tree[map[nodeKey]*TemplInfo]) (*TemplInfo, error) {
-	key, _, category, d := s.toKeyCategoryAndDescriptor(pi)
+	key, _, category, d, err := s.toKeyCategoryAndDescriptor(pi)
+	// See #13577. Warn for now.
+	if err != nil {
+		var loc string
+		if fi != nil {
+			loc = fmt.Sprintf("file %q", fi.Meta().Filename)
+		} else {
+			loc = fmt.Sprintf("path %q", pi.Path())
+		}
+		s.opts.Log.Warnf("skipping template %s: %s", loc, err)
+		return nil, nil
+	}
 
 	return s.insertTemplate2(pi, fi, key, category, d, replace, false, tree)
 }
@@ -1481,7 +1499,7 @@ func (s *TemplateStore) templates() iter.Seq[*TemplInfo] {
 	}
 }
 
-func (s *TemplateStore) toKeyCategoryAndDescriptor(p *paths.Path) (string, string, Category, TemplateDescriptor) {
+func (s *TemplateStore) toKeyCategoryAndDescriptor(p *paths.Path) (string, string, Category, TemplateDescriptor, error) {
 	k1 := p.Dir()
 	k2 := ""
 
@@ -1575,7 +1593,7 @@ func (s *TemplateStore) toKeyCategoryAndDescriptor(p *paths.Path) (string, strin
 		k1 = strings.TrimSuffix(k1, "/_markup")
 		parts := strings.Split(d.Layout, "-")
 		if len(parts) < 2 {
-			panic("markup template must have at least 2 parts")
+			return "", "", 0, TemplateDescriptor{}, fmt.Errorf("unrecognized render hook template")
 		}
 		// Either 2 or 3 parts, e.g. render-codeblock-go.
 		d.Variant1 = parts[1]
@@ -1585,7 +1603,7 @@ func (s *TemplateStore) toKeyCategoryAndDescriptor(p *paths.Path) (string, strin
 		d.Layout = "" // This allows using page layout as part of the key for lookups.
 	}
 
-	return k1, k2, category, d
+	return k1, k2, category, d, nil
 }
 
 func (s *TemplateStore) transformTemplates() error {
diff --git a/tpl/tplimpl/templatestore_integration_test.go b/tpl/tplimpl/templatestore_integration_test.go
index 2f38495621e..8e62df1d077 100644
--- a/tpl/tplimpl/templatestore_integration_test.go
+++ b/tpl/tplimpl/templatestore_integration_test.go
@@ -844,6 +844,21 @@ All.
 	b.AssertLogContains("Duplicate content path")
 }
 
+// Issue #13577.
+func TestPrintPathWarningOnInvalidMarkupFilename(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+-- layouts/all.html --
+All.
+-- layouts/_markup/sitemap.xml --
+`
+	b := hugolib.Test(t, files, hugolib.TestOptWarn())
+
+	b.AssertLogContains("unrecognized render hook")
+}
+
 func BenchmarkExecuteWithContext(b *testing.B) {
 	files := `
 -- hugo.toml --