Repository: gohugoio/hugo (language: go)

Issue:
`append` fails on `nil`
Given the following in a template file:

```
{{ $res := slice }}
{{ $res | append nil }}
```

... the build will fail with an error like:

```
... execute of template failed: template: "partials/openapi/render-api.html" at <append nil>: error calling append: reflect: call of reflect.Value.Type on zero Value
```

This previously worked with Hugo v1.113.0. I suspect it was introduced by https://github.com/gohugoio/hugo/pull/11104.


<!-- Please answer these questions before submitting your issue. Thanks! -->

### What version of Hugo are you using (`hugo version`)?

<pre>
v0.114.1
</pre>

### Does this issue reproduce with the latest release?

yes




Discussion / hints:

Fix patch:
diff --git a/common/collections/append.go b/common/collections/append.go
index 91abc46d348..8f1e21ea315 100644
--- a/common/collections/append.go
+++ b/common/collections/append.go
@@ -66,6 +66,10 @@ func Append(to any, from ...any) (any, error) {
 
 		if len(from) == 1 {
 			fromv := reflect.ValueOf(from[0])
+			if !fromv.IsValid() {
+				// from[0] is nil
+				return appendToInterfaceSliceFromValues(tov, fromv)
+			}
 			fromt := fromv.Type()
 			if fromt.Kind() == reflect.Slice {
 				fromt = fromt.Elem()
@@ -94,7 +98,7 @@ func Append(to any, from ...any) (any, error) {
 
 	for _, f := range from {
 		fv := reflect.ValueOf(f)
-		if !fv.Type().AssignableTo(tot) {
+		if !fv.IsValid() || !fv.Type().AssignableTo(tot) {
 			// Fall back to a []interface{} slice.
 			tov, _ := indirect(reflect.ValueOf(to))
 			return appendToInterfaceSlice(tov, from...)
@@ -109,6 +113,10 @@ func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, erro
 	var tos []any
 
 	for _, slice := range []reflect.Value{slice1, slice2} {
+		if !slice.IsValid() {
+			tos = append(tos, nil)
+			continue
+		}
 		for i := 0; i < slice.Len(); i++ {
 			tos = append(tos, slice.Index(i).Interface())
 		}


Test patch:
diff --git a/common/collections/append_test.go b/common/collections/append_test.go
index 415eb2f257e..3c2aab2db99 100644
--- a/common/collections/append_test.go
+++ b/common/collections/append_test.go
@@ -74,6 +74,9 @@ func TestAppend(t *testing.T) {
 			[]any{"c"},
 			false,
 		},
+		{[]string{"a", "b"}, []any{nil}, []any{"a", "b", nil}},
+		{[]string{"a", "b"}, []any{nil, "d", nil}, []any{"a", "b", nil, "d", nil}},
+		{[]any{"a", nil, "c"}, []any{"d", nil, "f"}, []any{"a", nil, "c", "d", nil, "f"}},
 	} {
 
 		result, err := Append(test.start, test.addend...)
diff --git a/tpl/collections/integration_test.go b/tpl/collections/integration_test.go
index da1d6e488a6..829aee355b4 100644
--- a/tpl/collections/integration_test.go
+++ b/tpl/collections/integration_test.go
@@ -104,3 +104,63 @@ func TestAppendSliceToASliceOfSlices(t *testing.T) {
 	}
 
 }
+
+func TestAppendNilToSlice(t *testing.T) {
+
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+-- layouts/index.html --
+{{ $obj := (slice "a") }}
+{{ $obj = $obj | append nil }}
+
+{{ $obj }}
+
+
+  `
+
+	for i := 0; i < 4; i++ {
+
+		b := hugolib.NewIntegrationTestBuilder(
+			hugolib.IntegrationTestConfig{
+				T:           t,
+				TxtarString: files,
+			},
+		).Build()
+
+		b.AssertFileContent("public/index.html", "[a &lt;nil&gt;]")
+
+	}
+
+}
+
+func TestAppendNilsToSliceWithNils(t *testing.T) {
+
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+-- layouts/index.html --
+{{ $obj := (slice "a" nil "c") }}
+{{ $obj = $obj | append nil }}
+
+{{ $obj }}
+
+
+  `
+
+	for i := 0; i < 4; i++ {
+
+		b := hugolib.NewIntegrationTestBuilder(
+			hugolib.IntegrationTestConfig{
+				T:           t,
+				TxtarString: files,
+			},
+		).Build()
+
+		b.AssertFileContent("public/index.html", "[a &lt;nil&gt; c &lt;nil&gt;]")
+
+	}
+
+}