From: Bjørn Erik Pedersen Date: Wed, 22 Oct 2025 09:06:54 +0000 (+0200) Subject: Fix "assignment to entry in nil map" on empty YAML config files X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=0579afc3c5f950328ea96e985ad5bc1279b0a938;p=brevno-suite%2Fhugo Fix "assignment to entry in nil map" on empty YAML config files Fixes #14074 --- diff --git a/common/herrors/errors.go b/common/herrors/errors.go index c7ee90dd0..693ff024c 100644 --- a/common/herrors/errors.go +++ b/common/herrors/errors.go @@ -17,22 +17,13 @@ package herrors import ( "errors" "fmt" - "io" "os" "regexp" - "runtime" "runtime/debug" "strings" "time" ) -// PrintStackTrace prints the current stacktrace to w. -func PrintStackTrace(w io.Writer) { - buf := make([]byte, 1<<16) - runtime.Stack(buf, true) - fmt.Fprintf(w, "%s", buf) -} - // ErrorSender is a, typically, non-blocking error handler. type ErrorSender interface { SendError(err error) diff --git a/config/allconfig/load.go b/config/allconfig/load.go index 318a1700a..b76c4abde 100644 --- a/config/allconfig/load.go +++ b/config/allconfig/load.go @@ -19,6 +19,7 @@ import ( "fmt" "os" "path/filepath" + "runtime/debug" "strings" "github.com/gobwas/glob" @@ -44,6 +45,7 @@ func LoadConfig(d ConfigSourceDescriptor) (configs *Configs, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("failed to load config: %v", r) + debug.PrintStack() } }() diff --git a/hugolib/config_test.go b/hugolib/config_test.go index 27e5f6d41..be5086f3e 100644 --- a/hugolib/config_test.go +++ b/hugolib/config_test.go @@ -1649,3 +1649,16 @@ Params: {{ site.Params }}| } } + +func TestConfigYAMLNilMapIssue14074(t *testing.T) { + t.Parallel() + + files := ` +-- config/_default/taxonomies.yaml -- +# empty on purpose +-- hugo.yaml -- + +` + + Test(t, files) +} diff --git a/parser/metadecoders/decoder.go b/parser/metadecoders/decoder.go index 18aaabce1..6443bfdf8 100644 --- a/parser/metadecoders/decoder.go +++ b/parser/metadecoders/decoder.go @@ -86,6 +86,15 @@ func (d Decoder) UnmarshalToMap(data []byte, f Format) (map[string]any, error) { err := d.UnmarshalTo(data, f, &m) + if m == nil { + // We migrated to github.com/goccy/go-yaml in v0.152.0, + // which produces nil maps for empty YAML files (and empty map nodes), unlike gopkg.in/yaml.v2. + // + // To prevent crashes when trying to handle empty config files etc., we ensure we always return a non-nil map here. + // See issue 14074. + m = make(map[string]any) + } + return m, err } diff --git a/parser/metadecoders/decoder_integration_test.go b/parser/metadecoders/decoder_integration_test.go new file mode 100644 index 000000000..2e533c5e4 --- /dev/null +++ b/parser/metadecoders/decoder_integration_test.go @@ -0,0 +1,43 @@ +// Copyright 2025 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metadecoders_test + +import ( + "testing" + + "github.com/gohugoio/hugo/hugolib" +) + +func TestYAMLIntegerSortIssue14078(t *testing.T) { + files := ` +-- assets/mydata.yaml -- +a: + weight: 1 +x: + weight: 2 +c: + weight: 3 +t: + weight: 4 + +-- layouts/all.html -- +{{ $mydata := resources.Get "mydata.yaml" | transform.Unmarshal }} +Sorted: {{ sort $mydata "weight" }}| + +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", "Sorted: [map[weight:1] map[weight:2] map[weight:3] map[weight:4]]|") +}