Improve error messages, esp. when the server is running - hugo - [fork] hugo port for 9front
 (HTM) git clone git@git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit f2946da9e806c2bafbdd26707fe339db79bd980b
 (DIR) parent 6eea32bd6bc8e7a7dd07a8cb6a8343ae2c74aba0
 (HTM) Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Mon,  2 May 2022 16:07:52 +0200
       
       Improve error messages, esp. when the server is running
       
       * Add file context to minifier errors when publishing
       * Misc fixes (see issues)
       * Allow custom server error template in layouts/server/error.html
       
       To get to this, this commit also cleans up and simplifies the code surrounding errors and files. This also removes the usage of `github.com/pkg/errors`, mostly because of https://github.com/pkg/errors/issues/223 -- but also because most of this is now built-in to Go.
       
       Fixes #9852
       Fixes #9857
       Fixes #9863
       
       Diffstat:
         M cache/filecache/filecache_config.go |      14 ++++++++------
         M cache/filecache/filecache_pruner.go |       4 ++--
         M commands/commandeer.go              |      17 ++++-------------
         M commands/convert.go                 |       4 +---
         M commands/hugo.go                    |      19 +++++++------------
         M commands/new_site.go                |       5 ++---
         M commands/server.go                  |      30 +++++++++++++++++++++---------
         M commands/server_errors.go           |      61 -------------------------------
         M common/herrors/error_locator.go     |     132 ++-----------------------------
         M common/herrors/error_locator_test.… |      38 +++++++++++++++++--------------
         M common/herrors/errors.go            |      26 --------------------------
         M common/herrors/file_error.go        |     253 ++++++++++++++++++++++++-------
         M common/herrors/file_error_test.go   |      46 +++++++++++++++++++++++++------
         M common/herrors/line_number_extract… |      24 +++++-------------------
         M common/hugio/copy.go                |       5 ++---
         M config/commonConfig.go              |       5 ++---
         M config/configLoader.go              |       7 +++----
         M create/content.go                   |      14 +++++++-------
         M deploy/deploy.go                    |       3 ++-
         M deploy/deployConfig.go              |       3 ++-
         M deps/deps.go                        |      13 ++++++-------
         M helpers/path.go                     |       3 +--
         M hugofs/decorators.go                |       5 ++---
         M hugofs/fileinfo.go                  |       2 +-
         M hugofs/rootmapping_fs.go            |       6 ++----
         M hugofs/slice_fs.go                  |       5 +++--
         M hugofs/walk.go                      |       8 ++++----
         M hugofs/walk_test.go                 |       2 +-
         M hugolib/config.go                   |       5 +++--
         M hugolib/configdir_test.go           |       2 +-
         M hugolib/content_factory.go          |      10 +++++-----
         M hugolib/content_map.go              |       3 +--
         M hugolib/content_map_page.go         |       7 +++----
         M hugolib/fileInfo.go                 |       5 ++---
         M hugolib/filesystems/basefs.go       |      10 ++++------
         M hugolib/hugo_sites.go               |      39 ++++++++++++++-----------------
         M hugolib/hugo_sites_build.go         |      10 +++++-----
         M hugolib/hugo_sites_build_errors_te… |     108 ++++++++++++++++++++++++++------
         M hugolib/integrationtest_builder.go  |       3 +--
         M hugolib/page.go                     |      78 ++++++++++++++++++++-----------
         M hugolib/page__meta.go               |       3 +--
         M hugolib/page__per_output.go         |       7 ++++---
         M hugolib/page__ref.go                |       5 ++---
         M hugolib/page_unwrap.go              |       4 ++--
         M hugolib/pages_process.go            |       3 +--
         M hugolib/paths/paths.go              |       3 +--
         M hugolib/shortcode.go                |      31 ++++++++++++++++++-------------
         M hugolib/shortcode_test.go           |       4 ++--
         M hugolib/site.go                     |      14 ++++++--------
         M hugolib/site_render.go              |       5 +++--
         M hugolib/testhelpers_test.go         |      10 +++-------
         M langs/config.go                     |       4 ++--
         M langs/i18n/translationProvider.go   |      15 +++++----------
         M langs/language.go                   |       5 ++---
         M lazy/init.go                        |       2 +-
         M markup/blackfriday/blackfriday_con… |       5 +++--
         M modules/client.go                   |      33 ++++++++++++++++---------------
         M modules/collect.go                  |      18 +++++++++---------
         M modules/config.go                   |       4 +---
         M modules/npm/package_builder.go      |      14 ++++++--------
         M navigation/menu.go                  |       4 +---
         M navigation/pagemenus.go             |       5 +++--
         M output/outputFormat.go              |       6 ++----
         M parser/metadecoders/decoder.go      |      19 +++++++++----------
         M parser/pageparser/pageparser.go     |       4 ++--
         M publisher/publisher.go              |       3 ++-
         M releaser/releaser.go                |       5 +++--
         M resources/image.go                  |       7 ++-----
         M resources/images/color.go           |       5 ++---
         M resources/images/config.go          |       6 +++---
         M resources/images/image.go           |       7 ++++---
         M resources/page/page_generate/gener… |       8 ++++----
         M resources/page/page_matcher.go      |       4 ++--
         M resources/page/pages_related.go     |       4 ++--
         M resources/page/permalinks.go        |       2 +-
         M resources/resource.go               |       4 ++--
         M resources/resource_factories/creat… |      14 +++++++-------
         M resources/resource_metadata.go      |       3 +--
         M resources/resource_transformers/ba… |       6 +++---
         M resources/resource_transformers/in… |       5 ++---
         M resources/resource_transformers/js… |      15 +++++++++------
         M resources/resource_transformers/js… |       5 ++---
         M resources/resource_transformers/po… |      16 ++++++----------
         M resources/resource_transformers/te… |       5 +++--
         M resources/resource_transformers/to… |      12 +-----------
         M resources/resource_transformers/to… |       5 ++---
         M resources/transform.go              |       8 +++-----
         M source/fileInfo.go                  |       7 +++----
         M source/filesystem.go                |       5 ++---
         M tpl/collections/collections.go      |       5 +++--
         M tpl/collections/merge.go            |       9 +++++----
         M tpl/collections/reflect_helpers.go  |       5 +++--
         M tpl/collections/symdiff.go          |       4 +---
         M tpl/data/data.go                    |       8 ++++----
         M tpl/data/resources.go               |       5 ++---
         M tpl/images/images.go                |       2 +-
         M tpl/internal/resourcehelpers/helpe… |       4 +---
         M tpl/lang/lang.go                    |       5 +++--
         M tpl/openapi/openapi3/openapi3.go    |       5 +++--
         M tpl/resources/resources.go          |       7 ++++---
         M tpl/strings/strings.go              |      14 +++++++-------
         A tpl/tplimpl/embedded/templates/ser… |      63 +++++++++++++++++++++++++++++++
         M tpl/tplimpl/template.go             |      15 ++++++---------
         M tpl/tplimpl/template_ast_transform… |      10 ++++++----
         M tpl/tplimpl/template_errors.go      |      20 ++++++++++----------
         M tpl/transform/remarshal.go          |       2 +-
         M tpl/transform/unmarshal.go          |      12 +++++++-----
         M tpl/urls/urls.go                    |       3 +--
         M transform/chain.go                  |      15 ++++++++++++++-
       
       109 files changed, 858 insertions(+), 777 deletions(-)
       ---
 (DIR) diff --git a/cache/filecache/filecache_config.go b/cache/filecache/filecache_config.go
       @@ -14,6 +14,7 @@
        package filecache
        
        import (
       +        "fmt"
                "path"
                "path/filepath"
                "strings"
       @@ -25,8 +26,9 @@ import (
        
                "github.com/gohugoio/hugo/helpers"
        
       +        "errors"
       +
                "github.com/mitchellh/mapstructure"
       -        "github.com/pkg/errors"
                "github.com/spf13/afero"
        )
        
       @@ -153,7 +155,7 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
                        }
        
                        if err := decoder.Decode(v); err != nil {
       -                        return nil, errors.Wrap(err, "failed to decode filecache config")
       +                        return nil, fmt.Errorf("failed to decode filecache config: %w", err)
                        }
        
                        if cc.Dir == "" {
       @@ -162,7 +164,7 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
        
                        name := strings.ToLower(k)
                        if !valid[name] {
       -                        return nil, errors.Errorf("%q is not a valid cache name", name)
       +                        return nil, fmt.Errorf("%q is not a valid cache name", name)
                        }
        
                        c[name] = cc
       @@ -197,12 +199,12 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
        
                        if !v.isResourceDir {
                                if isOsFs && !filepath.IsAbs(v.Dir) {
       -                                return c, errors.Errorf("%q must resolve to an absolute directory", v.Dir)
       +                                return c, fmt.Errorf("%q must resolve to an absolute directory", v.Dir)
                                }
        
                                // Avoid cache in root, e.g. / (Unix) or c:\ (Windows)
                                if len(strings.TrimPrefix(v.Dir, filepath.VolumeName(v.Dir))) == 1 {
       -                                return c, errors.Errorf("%q is a root folder and not allowed as cache dir", v.Dir)
       +                                return c, fmt.Errorf("%q is a root folder and not allowed as cache dir", v.Dir)
                                }
                        }
        
       @@ -242,5 +244,5 @@ func resolveDirPlaceholder(fs afero.Fs, cfg config.Provider, placeholder string)
                        return filepath.Base(workingDir), false, nil
                }
        
       -        return "", false, errors.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder)
       +        return "", false, fmt.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder)
        }
 (DIR) diff --git a/cache/filecache/filecache_pruner.go b/cache/filecache/filecache_pruner.go
       @@ -14,12 +14,12 @@
        package filecache
        
        import (
       +        "fmt"
                "io"
                "os"
        
                "github.com/gohugoio/hugo/hugofs"
        
       -        "github.com/pkg/errors"
                "github.com/spf13/afero"
        )
        
       @@ -39,7 +39,7 @@ func (c Caches) Prune() (int, error) {
                                if os.IsNotExist(err) {
                                        continue
                                }
       -                        return counter, errors.Wrapf(err, "failed to prune cache %q", k)
       +                        return counter, fmt.Errorf("failed to prune cache %q: %w", k, err)
                        }
        
                }
 (DIR) diff --git a/commands/commandeer.go b/commands/commandeer.go
       @@ -14,7 +14,6 @@
        package commands
        
        import (
       -        "bytes"
                "errors"
                "io/ioutil"
                "net"
       @@ -141,19 +140,11 @@ func (c *commandeer) getErrorWithContext() any {
        
                m := make(map[string]any)
        
       -        m["Error"] = errors.New(removeErrorPrefixFromLog(c.logger.Errors()))
       +        //xwm["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.logger.Errors())))
       +        m["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.logger.Errors())))
                m["Version"] = hugo.BuildVersionString()
       -
       -        fe := herrors.UnwrapErrorWithFileContext(c.buildErr)
       -        if fe != nil {
       -                m["File"] = fe
       -        }
       -
       -        if c.h.verbose {
       -                var b bytes.Buffer
       -                herrors.FprintStackTraceFromErr(&b, c.buildErr)
       -                m["StackTrace"] = b.String()
       -        }
       +        ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.buildErr)
       +        m["Files"] = ferrors
        
                return m
        }
 (DIR) diff --git a/commands/convert.go b/commands/convert.go
       @@ -31,8 +31,6 @@ import (
                "github.com/gohugoio/hugo/parser"
                "github.com/gohugoio/hugo/parser/metadecoders"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/hugolib"
        
                "github.com/spf13/cobra"
       @@ -193,7 +191,7 @@ func (cc *convertCmd) convertAndSavePage(p page.Page, site *hugolib.Site, target
        
                fs := hugofs.Os
                if err := helpers.WriteToDisk(newFilename, &newContent, fs); err != nil {
       -                return errors.Wrapf(err, "Failed to save file %q:", newFilename)
       +                return fmt.Errorf("Failed to save file %q:: %w", newFilename, err)
                }
        
                return nil
 (DIR) diff --git a/commands/hugo.go b/commands/hugo.go
       @@ -39,9 +39,6 @@ import (
        
                "github.com/gohugoio/hugo/resources/page"
        
       -        "github.com/pkg/errors"
       -
       -        "github.com/gohugoio/hugo/common/herrors"
                "github.com/gohugoio/hugo/common/hugo"
                "github.com/gohugoio/hugo/common/loggers"
                "github.com/gohugoio/hugo/common/terminal"
       @@ -300,14 +297,14 @@ func (c *commandeer) fullBuild(noBuildLock bool) error {
                copyStaticFunc := func() error {
                        cnt, err := c.copyStatic()
                        if err != nil {
       -                        return errors.Wrap(err, "Error copying static files")
       +                        return fmt.Errorf("Error copying static files: %w", err)
                        }
                        langCount = cnt
                        return nil
                }
                buildSitesFunc := func() error {
                        if err := c.buildSites(noBuildLock); err != nil {
       -                        return errors.Wrap(err, "Error building site")
       +                        return fmt.Errorf("Error building site: %w", err)
                        }
                        return nil
                }
       @@ -354,10 +351,10 @@ func (c *commandeer) initCPUProfile() (func(), error) {
        
                f, err := os.Create(c.h.cpuprofile)
                if err != nil {
       -                return nil, errors.Wrap(err, "failed to create CPU profile")
       +                return nil, fmt.Errorf("failed to create CPU profile: %w", err)
                }
                if err := pprof.StartCPUProfile(f); err != nil {
       -                return nil, errors.Wrap(err, "failed to start CPU profile")
       +                return nil, fmt.Errorf("failed to start CPU profile: %w", err)
                }
                return func() {
                        pprof.StopCPUProfile()
       @@ -388,11 +385,11 @@ func (c *commandeer) initTraceProfile() (func(), error) {
        
                f, err := os.Create(c.h.traceprofile)
                if err != nil {
       -                return nil, errors.Wrap(err, "failed to create trace file")
       +                return nil, fmt.Errorf("failed to create trace file: %w", err)
                }
        
                if err := trace.Start(f); err != nil {
       -                return nil, errors.Wrap(err, "failed to start trace")
       +                return nil, fmt.Errorf("failed to start trace: %w", err)
                }
        
                return func() {
       @@ -735,9 +732,7 @@ func (c *commandeer) handleBuildErr(err error, msg string) {
        
                c.logger.Errorln(msg + ":\n")
                c.logger.Errorln(helpers.FirstUpper(err.Error()))
       -        if !c.h.quiet && c.h.verbose {
       -                herrors.PrintStackTraceFromErr(err)
       -        }
       +
        }
        
        func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
 (DIR) diff --git a/commands/new_site.go b/commands/new_site.go
       @@ -16,14 +16,13 @@ package commands
        import (
                "bytes"
                "errors"
       +        "fmt"
                "path/filepath"
                "strings"
        
                "github.com/gohugoio/hugo/config"
                "github.com/gohugoio/hugo/parser/metadecoders"
        
       -        _errors "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/create"
                "github.com/gohugoio/hugo/helpers"
                "github.com/gohugoio/hugo/hugofs"
       @@ -94,7 +93,7 @@ func (n *newSiteCmd) doNewSite(fs *hugofs.Fs, basepath string, force bool) error
        
                for _, dir := range dirs {
                        if err := fs.Source.MkdirAll(dir, 0777); err != nil {
       -                        return _errors.Wrap(err, "Failed to create dir")
       +                        return fmt.Errorf("Failed to create dir: %w", err)
                        }
                }
        
 (DIR) diff --git a/commands/server.go b/commands/server.go
       @@ -36,8 +36,6 @@ import (
                "github.com/gohugoio/hugo/common/paths"
                "golang.org/x/sync/errgroup"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/livereload"
        
                "github.com/gohugoio/hugo/config"
       @@ -366,7 +364,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
                // We're only interested in the path
                u, err := url.Parse(baseURL)
                if err != nil {
       -                return nil, nil, "", "", errors.Wrap(err, "Invalid baseURL")
       +                return nil, nil, "", "", fmt.Errorf("Invalid baseURL: %w", err)
                }
        
                decorate := func(h http.Handler) http.Handler {
       @@ -480,6 +478,21 @@ var logErrorRe = regexp.MustCompile(`(?s)ERROR \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{
        func removeErrorPrefixFromLog(content string) string {
                return logErrorRe.ReplaceAllLiteralString(content, "")
        }
       +func cleanErrorLog(content string) string {
       +        content = strings.ReplaceAll(content, "Rebuild failed:\n", "")
       +        content = strings.ReplaceAll(content, "\n", "")
       +        seen := make(map[string]bool)
       +        parts := strings.Split(content, ": ")
       +        keep := make([]string, 0, len(parts))
       +        for _, part := range parts {
       +                if seen[part] {
       +                        continue
       +                }
       +                seen[part] = true
       +                keep = append(keep, part)
       +        }
       +        return strings.Join(keep, ": ")
       +}
        
        func (c *commandeer) serve(s *serverCmd) error {
                isMultiHost := c.hugo().IsMultihost()
       @@ -500,17 +513,16 @@ func (c *commandeer) serve(s *serverCmd) error {
                        roots = []string{""}
                }
        
       -        templ, err := c.hugo().TextTmpl().Parse("__default_server_error", buildErrorTemplate)
       -        if err != nil {
       -                return err
       -        }
       -
                srv := &fileServer{
                        baseURLs: baseURLs,
                        roots:    roots,
                        c:        c,
                        s:        s,
                        errorTemplate: func(ctx any) (io.Reader, error) {
       +                        templ, found := c.hugo().Tmpl().Lookup("server/error.html")
       +                        if !found {
       +                                panic("template server/error.html not found")
       +                        }
                                b := &bytes.Buffer{}
                                err := c.hugo().Tmpl().Execute(templ, b, ctx)
                                return b, err
       @@ -627,7 +639,7 @@ func (sc *serverCmd) fixURL(cfg config.Provider, s string, port int) (string, er
                        if strings.Contains(u.Host, ":") {
                                u.Host, _, err = net.SplitHostPort(u.Host)
                                if err != nil {
       -                                return "", errors.Wrap(err, "Failed to split baseURL hostpost")
       +                                return "", fmt.Errorf("Failed to split baseURL hostpost: %w", err)
                                }
                        }
                        u.Host += fmt.Sprintf(":%d", port)
 (DIR) diff --git a/commands/server_errors.go b/commands/server_errors.go
       @@ -22,67 +22,6 @@ import (
                "github.com/gohugoio/hugo/transform/livereloadinject"
        )
        
       -var buildErrorTemplate = `<!doctype html>
       -<html class="no-js" lang="">
       -        <head>
       -                <meta charset="utf-8">
       -                <title>Hugo Server: Error</title>
       -                <style type="text/css">
       -                body {
       -                        font-family: "Muli",avenir, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
       -                        font-size: 16px;
       -                        background-color: #2f1e2e;
       -                }
       -                main {
       -                        margin: auto;
       -                        width: 95%;
       -                        padding: 1rem;
       -                }                
       -                .version {
       -                        color: #ccc;
       -                        padding: 1rem 0;
       -                }
       -                .stack {
       -                        margin-top: 4rem;
       -                }
       -                pre {
       -                        white-space: pre-wrap;      
       -                        white-space: -moz-pre-wrap;  
       -                        white-space: -pre-wrap;     
       -                        white-space: -o-pre-wrap;    
       -                        word-wrap: break-word;     
       -                }
       -                .highlight {
       -                        overflow-x: auto;
       -                        margin-bottom: 1rem;
       -                }
       -                a {
       -                        color: #0594cb;
       -                        text-decoration: none;
       -                }
       -                a:hover {
       -                        color: #ccc;
       -                }
       -                </style>
       -        </head>
       -        <body>
       -                <main>
       -                        {{ highlight .Error "apl" "linenos=false,noclasses=true,style=paraiso-dark" }}
       -                        {{ with .File }}
       -                        {{ $params := printf "noclasses=true,style=paraiso-dark,linenos=table,hl_lines=%d,linenostart=%d" (add .LinesPos 1) (sub .Position.LineNumber .LinesPos) }}
       -                        {{ $lexer := .ChromaLexer | default "go-html-template" }}
       -                        {{  highlight (delimit .Lines "\n") $lexer $params }}
       -                        {{ end }}
       -                        {{ with .StackTrace }}
       -                        {{ highlight . "apl" "noclasses=true,style=paraiso-dark" }}
       -                        {{ end }}
       -                        <p class="version">{{ .Version }}</p>
       -                        <a href="">Reload Page</a>
       -                </main>
       -</body>
       -</html>
       -`
       -
        func injectLiveReloadScript(src io.Reader, baseURL url.URL) string {
                var b bytes.Buffer
                chain := transform.Chain{livereloadinject.New(baseURL)}
 (DIR) diff --git a/common/herrors/error_locator.go b/common/herrors/error_locator.go
       @@ -1,4 +1,4 @@
       -// Copyright 2018 The Hugo Authors. All rights reserved.
       +// Copyright 2022 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.
       @@ -21,8 +21,6 @@ import (
                "strings"
        
                "github.com/gohugoio/hugo/common/text"
       -
       -        "github.com/spf13/afero"
        )
        
        // LineMatcher contains the elements used to match an error to a line
       @@ -43,8 +41,6 @@ var SimpleLineMatcher = func(m LineMatcher) bool {
                return m.Position.LineNumber == m.LineNumber
        }
        
       -var _ text.Positioner = ErrorContext{}
       -
        // ErrorContext contains contextual information about an error. This will
        // typically be the lines surrounding some problem in a file.
        type ErrorContext struct {
       @@ -56,125 +52,11 @@ type ErrorContext struct {
                // The position of the error in the Lines above. 0 based.
                LinesPos int
        
       -        position text.Position
       -
                // The lexer to use for syntax highlighting.
                // https://gohugo.io/content-management/syntax-highlighting/#list-of-chroma-highlighting-languages
                ChromaLexer string
        }
        
       -// Position returns the text position of this error.
       -func (e ErrorContext) Position() text.Position {
       -        return e.position
       -}
       -
       -var _ causer = (*ErrorWithFileContext)(nil)
       -
       -// ErrorWithFileContext is an error with some additional file context related
       -// to that error.
       -type ErrorWithFileContext struct {
       -        cause error
       -        ErrorContext
       -}
       -
       -func (e *ErrorWithFileContext) Error() string {
       -        pos := e.Position()
       -        if pos.IsValid() {
       -                return pos.String() + ": " + e.cause.Error()
       -        }
       -        return e.cause.Error()
       -}
       -
       -func (e *ErrorWithFileContext) Cause() error {
       -        return e.cause
       -}
       -
       -// WithFileContextForFile will try to add a file context with lines matching the given matcher.
       -// If no match could be found, the original error is returned with false as the second return value.
       -func WithFileContextForFile(e error, realFilename, filename string, fs afero.Fs, matcher LineMatcherFn) (error, bool) {
       -        f, err := fs.Open(filename)
       -        if err != nil {
       -                return e, false
       -        }
       -        defer f.Close()
       -        return WithFileContext(e, realFilename, f, matcher)
       -}
       -
       -// WithFileContextForFileDefault tries to add file context using the default line matcher.
       -func WithFileContextForFileDefault(err error, filename string, fs afero.Fs) error {
       -        err, _ = WithFileContextForFile(
       -                err,
       -                filename,
       -                filename,
       -                fs,
       -                SimpleLineMatcher)
       -        return err
       -}
       -
       -// WithFileContextForFile will try to add a file context with lines matching the given matcher.
       -// If no match could be found, the original error is returned with false as the second return value.
       -func WithFileContext(e error, realFilename string, r io.Reader, matcher LineMatcherFn) (error, bool) {
       -        if e == nil {
       -                panic("error missing")
       -        }
       -        le := UnwrapFileError(e)
       -
       -        if le == nil {
       -                var ok bool
       -                if le, ok = ToFileError("", e).(FileError); !ok {
       -                        return e, false
       -                }
       -        }
       -
       -        var errCtx ErrorContext
       -
       -        posle := le.Position()
       -
       -        if posle.Offset != -1 {
       -                errCtx = locateError(r, le, func(m LineMatcher) bool {
       -                        if posle.Offset >= m.Offset && posle.Offset < m.Offset+len(m.Line) {
       -                                lno := posle.LineNumber - m.Position.LineNumber + m.LineNumber
       -                                m.Position = text.Position{LineNumber: lno}
       -                        }
       -                        return matcher(m)
       -                })
       -        } else {
       -                errCtx = locateError(r, le, matcher)
       -        }
       -
       -        pos := &errCtx.position
       -
       -        if pos.LineNumber == -1 {
       -                return e, false
       -        }
       -
       -        pos.Filename = realFilename
       -
       -        if le.Type() != "" {
       -                errCtx.ChromaLexer = chromaLexerFromType(le.Type())
       -        } else {
       -                errCtx.ChromaLexer = chromaLexerFromFilename(realFilename)
       -        }
       -
       -        return &ErrorWithFileContext{cause: e, ErrorContext: errCtx}, true
       -}
       -
       -// UnwrapErrorWithFileContext tries to unwrap an ErrorWithFileContext from err.
       -// It returns nil if this is not possible.
       -func UnwrapErrorWithFileContext(err error) *ErrorWithFileContext {
       -        for err != nil {
       -                switch v := err.(type) {
       -                case *ErrorWithFileContext:
       -                        return v
       -                case causer:
       -                        err = v.Cause()
       -                default:
       -                        return nil
       -                }
       -        }
       -        return nil
       -}
       -
        func chromaLexerFromType(fileType string) string {
                switch fileType {
                case "html", "htm":
       @@ -196,23 +78,23 @@ func chromaLexerFromFilename(filename string) string {
                return chromaLexerFromType(ext)
        }
        
       -func locateErrorInString(src string, matcher LineMatcherFn) ErrorContext {
       +func locateErrorInString(src string, matcher LineMatcherFn) (*ErrorContext, text.Position) {
                return locateError(strings.NewReader(src), &fileError{}, matcher)
        }
        
       -func locateError(r io.Reader, le FileError, matches LineMatcherFn) ErrorContext {
       +func locateError(r io.Reader, le FileError, matches LineMatcherFn) (*ErrorContext, text.Position) {
                if le == nil {
                        panic("must provide an error")
                }
        
       -        errCtx := ErrorContext{position: text.Position{LineNumber: -1, ColumnNumber: 1, Offset: -1}, LinesPos: -1}
       +        errCtx := &ErrorContext{LinesPos: -1}
       +        pos := text.Position{LineNumber: -1, ColumnNumber: 1, Offset: -1}
        
                b, err := ioutil.ReadAll(r)
                if err != nil {
       -                return errCtx
       +                return errCtx, pos
                }
        
       -        pos := &errCtx.position
                lepos := le.Position()
        
                lines := strings.Split(string(b), "\n")
       @@ -262,5 +144,5 @@ func locateError(r io.Reader, le FileError, matches LineMatcherFn) ErrorContext 
        
                }
        
       -        return errCtx
       +        return errCtx, pos
        }
 (DIR) diff --git a/common/herrors/error_locator_test.go b/common/herrors/error_locator_test.go
       @@ -1,4 +1,4 @@
       -// Copyright 2018 The Hugo Authors. All rights reserved.
       +// Copyright 2022 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.
       @@ -38,44 +38,48 @@ LINE 7
        LINE 8
        `
        
       -        location := locateErrorInString(lines, lineMatcher)
       +        location, pos := locateErrorInString(lines, lineMatcher)
                c.Assert(location.Lines, qt.DeepEquals, []string{"LINE 3", "LINE 4", "This is THEONE", "LINE 6", "LINE 7"})
        
       -        pos := location.Position()
                c.Assert(pos.LineNumber, qt.Equals, 5)
                c.Assert(location.LinesPos, qt.Equals, 2)
        
       -        c.Assert(locateErrorInString(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"})
       +        locate := func(s string, m LineMatcherFn) *ErrorContext {
       +                ctx, _ := locateErrorInString(s, m)
       +                return ctx
       +        }
       +
       +        c.Assert(locate(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"})
        
       -        location = locateErrorInString(`L1
       +        location, pos = locateErrorInString(`L1
        This is THEONE
        L2
        `, lineMatcher)
       -        c.Assert(location.Position().LineNumber, qt.Equals, 2)
       +        c.Assert(pos.LineNumber, qt.Equals, 2)
                c.Assert(location.LinesPos, qt.Equals, 1)
                c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This is THEONE", "L2", ""})
        
       -        location = locateErrorInString(`This is THEONE
       +        location = locate(`This is THEONE
        L2
        `, lineMatcher)
                c.Assert(location.LinesPos, qt.Equals, 0)
                c.Assert(location.Lines, qt.DeepEquals, []string{"This is THEONE", "L2", ""})
        
       -        location = locateErrorInString(`L1
       +        location = locate(`L1
        This THEONE
        `, lineMatcher)
                c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This THEONE", ""})
                c.Assert(location.LinesPos, qt.Equals, 1)
        
       -        location = locateErrorInString(`L1
       +        location = locate(`L1
        L2
        This THEONE
        `, lineMatcher)
                c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "L2", "This THEONE", ""})
                c.Assert(location.LinesPos, qt.Equals, 2)
        
       -        location = locateErrorInString("NO MATCH", lineMatcher)
       -        c.Assert(location.Position().LineNumber, qt.Equals, -1)
       +        location, pos = locateErrorInString("NO MATCH", lineMatcher)
       +        c.Assert(pos.LineNumber, qt.Equals, -1)
                c.Assert(location.LinesPos, qt.Equals, -1)
                c.Assert(len(location.Lines), qt.Equals, 0)
        
       @@ -83,7 +87,7 @@ This THEONE
                        return m.LineNumber == 6
                }
        
       -        location = locateErrorInString(`A
       +        location, pos = locateErrorInString(`A
        B
        C
        D
       @@ -95,7 +99,7 @@ I
        J`, lineMatcher)
        
                c.Assert(location.Lines, qt.DeepEquals, []string{"D", "E", "F", "G", "H"})
       -        c.Assert(location.Position().LineNumber, qt.Equals, 6)
       +        c.Assert(pos.LineNumber, qt.Equals, 6)
                c.Assert(location.LinesPos, qt.Equals, 2)
        
                // Test match EOF
       @@ -103,26 +107,26 @@ J`, lineMatcher)
                        return m.LineNumber == 4
                }
        
       -        location = locateErrorInString(`A
       +        location, pos = locateErrorInString(`A
        B
        C
        `, lineMatcher)
        
                c.Assert(location.Lines, qt.DeepEquals, []string{"B", "C", ""})
       -        c.Assert(location.Position().LineNumber, qt.Equals, 4)
       +        c.Assert(pos.LineNumber, qt.Equals, 4)
                c.Assert(location.LinesPos, qt.Equals, 2)
        
                offsetMatcher := func(m LineMatcher) bool {
                        return m.Offset == 1
                }
        
       -        location = locateErrorInString(`A
       +        location, pos = locateErrorInString(`A
        B
        C
        D
        E`, offsetMatcher)
        
                c.Assert(location.Lines, qt.DeepEquals, []string{"A", "B", "C", "D"})
       -        c.Assert(location.Position().LineNumber, qt.Equals, 2)
       +        c.Assert(pos.LineNumber, qt.Equals, 2)
                c.Assert(location.LinesPos, qt.Equals, 1)
        }
 (DIR) diff --git a/common/herrors/errors.go b/common/herrors/errors.go
       @@ -19,37 +19,11 @@ import (
                "errors"
                "fmt"
                "io"
       -        "os"
                "runtime"
                "runtime/debug"
                "strconv"
       -
       -        _errors "github.com/pkg/errors"
        )
        
       -// As defined in https://godoc.org/github.com/pkg/errors
       -type causer interface {
       -        Cause() error
       -}
       -
       -type stackTracer interface {
       -        StackTrace() _errors.StackTrace
       -}
       -
       -// PrintStackTraceFromErr prints the error's stack trace to stdoud.
       -func PrintStackTraceFromErr(err error) {
       -        FprintStackTraceFromErr(os.Stdout, err)
       -}
       -
       -// FprintStackTraceFromErr prints the error's stack trace to w.
       -func FprintStackTraceFromErr(w io.Writer, err error) {
       -        if err, ok := err.(stackTracer); ok {
       -                for _, f := range err.StackTrace() {
       -                        fmt.Fprintf(w, "%+s:%d\n", f, f)
       -                }
       -        }
       -}
       -
        // PrintStackTrace prints the current stacktrace to w.
        func PrintStackTrace(w io.Writer) {
                buf := make([]byte, 1<<16)
 (DIR) diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go
       @@ -1,11 +1,11 @@
       -// Copyright 2018 The Hugo Authors. All rights reserved.
       +// Copyright 2022 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
       +// Unless required by applicable lfmtaw 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
       @@ -15,59 +15,217 @@ package herrors
        
        import (
                "encoding/json"
       +        "fmt"
       +        "io"
       +        "path/filepath"
        
       +        "github.com/gohugoio/hugo/common/paths"
                "github.com/gohugoio/hugo/common/text"
       +        "github.com/pelletier/go-toml/v2"
       +        "github.com/spf13/afero"
       +        "github.com/tdewolff/parse/v2"
        
       -        "github.com/pkg/errors"
       +        "errors"
        )
        
       -var _ causer = (*fileError)(nil)
       -
        // FileError represents an error when handling a file: Parsing a config file,
        // execute a template etc.
        type FileError interface {
                error
        
       +        // ErroContext holds some context information about the error.
       +        ErrorContext() *ErrorContext
       +
                text.Positioner
        
       -        // A string identifying the type of file, e.g. JSON, TOML, markdown etc.
       -        Type() string
       +        // UpdatePosition updates the position of the error.
       +        UpdatePosition(pos text.Position) FileError
       +
       +        // UpdateContent updates the error with a new ErrorContext from the content of the file.
       +        UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError
       +}
       +
       +// Unwrapper can unwrap errors created with fmt.Errorf.
       +type Unwrapper interface {
       +        Unwrap() error
        }
        
       -var _ FileError = (*fileError)(nil)
       +var (
       +        _ FileError = (*fileError)(nil)
       +        _ Unwrapper = (*fileError)(nil)
       +)
       +
       +func (fe *fileError) UpdatePosition(pos text.Position) FileError {
       +        oldFilename := fe.Position().Filename
       +        if pos.Filename != "" && fe.fileType == "" {
       +                _, fe.fileType = paths.FileAndExtNoDelimiter(filepath.Clean(pos.Filename))
       +        }
       +        if pos.Filename == "" {
       +                pos.Filename = oldFilename
       +        }
       +        fe.position = pos
       +        return fe
       +}
       +
       +func (fe *fileError) UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError {
       +        if linematcher == nil {
       +                linematcher = SimpleLineMatcher
       +        }
       +
       +        var (
       +                contentPos   text.Position
       +                posle        = fe.position
       +                errorContext *ErrorContext
       +        )
       +
       +        if posle.LineNumber <= 1 && posle.Offset > 0 {
       +                // Try to locate the line number from the content if offset is set.
       +                errorContext, contentPos = locateError(r, fe, func(m LineMatcher) bool {
       +                        if posle.Offset >= m.Offset && posle.Offset < m.Offset+len(m.Line) {
       +                                lno := posle.LineNumber - m.Position.LineNumber + m.LineNumber
       +                                m.Position = text.Position{LineNumber: lno}
       +                                return linematcher(m)
       +                        }
       +                        return false
       +                })
       +        } else {
       +                errorContext, contentPos = locateError(r, fe, linematcher)
       +        }
       +
       +        if errorContext.ChromaLexer == "" {
       +                if fe.fileType != "" {
       +                        errorContext.ChromaLexer = chromaLexerFromType(fe.fileType)
       +                } else {
       +                        errorContext.ChromaLexer = chromaLexerFromFilename(fe.Position().Filename)
       +                }
       +        }
       +
       +        fe.errorContext = errorContext
       +
       +        if contentPos.LineNumber > 0 {
       +                fe.position.LineNumber = contentPos.LineNumber
       +        }
       +
       +        return fe
       +
       +}
        
        type fileError struct {
       -        position text.Position
       +        position     text.Position
       +        errorContext *ErrorContext
        
                fileType string
        
                cause error
        }
        
       +type fileErrorWithErrorContext struct {
       +        *fileError
       +}
       +
       +func (e *fileError) ErrorContext() *ErrorContext {
       +        return e.errorContext
       +}
       +
        // Position returns the text position of this error.
        func (e fileError) Position() text.Position {
                return e.position
        }
        
       -func (e *fileError) Type() string {
       -        return e.fileType
       +func (e *fileError) Error() string {
       +        return fmt.Sprintf("%s: %s", e.position, e.cause)
        }
        
       -func (e *fileError) Error() string {
       -        if e.cause == nil {
       -                return ""
       +func (e *fileError) Unwrap() error {
       +        return e.cause
       +}
       +
       +// NewFileError creates a new FileError that wraps err.
       +// The value for name should identify the file, the best
       +// being the full filename to the file on disk.
       +func NewFileError(name string, err error) FileError {
       +        if err == nil {
       +                panic("err is nil")
       +        }
       +
       +        // Filetype is used to determine the Chroma lexer to use.
       +        fileType, pos := extractFileTypePos(err)
       +        pos.Filename = name
       +        if fileType == "" {
       +                _, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(name))
                }
       -        return e.cause.Error()
       +
       +        if pos.LineNumber < 0 {
       +                panic(fmt.Sprintf("invalid line number: %d", pos.LineNumber))
       +        }
       +
       +        return &fileError{cause: err, fileType: fileType, position: pos}
       +
        }
        
       -func (f *fileError) Cause() error {
       -        return f.cause
       +// NewFileErrorFromFile is a convenience method to create a new FileError from a file.
       +func NewFileErrorFromFile(err error, filename, realFilename string, fs afero.Fs, linematcher LineMatcherFn) FileError {
       +        if err == nil {
       +                panic("err is nil")
       +        }
       +        if linematcher == nil {
       +                linematcher = SimpleLineMatcher
       +        }
       +        f, err2 := fs.Open(filename)
       +        if err2 != nil {
       +                return NewFileError(realFilename, err)
       +        }
       +        defer f.Close()
       +        return NewFileError(realFilename, err).UpdateContent(f, linematcher)
        }
        
       -// NewFileError creates a new FileError.
       -func NewFileError(fileType string, offset, lineNumber, columnNumber int, err error) FileError {
       -        pos := text.Position{Offset: offset, LineNumber: lineNumber, ColumnNumber: columnNumber}
       -        return &fileError{cause: err, fileType: fileType, position: pos}
       +// Cause returns the underlying error or itself if it does not implement Unwrap.
       +func Cause(err error) error {
       +        if u := errors.Unwrap(err); u != nil {
       +                return u
       +        }
       +        return err
       +}
       +
       +func extractFileTypePos(err error) (string, text.Position) {
       +        err = Cause(err)
       +        var fileType string
       +
       +        // Fall back to line/col 1:1 if we cannot find any better information.
       +        pos := text.Position{
       +                Offset:       -1,
       +                LineNumber:   1,
       +                ColumnNumber: 1,
       +        }
       +
       +        // JSON errors.
       +        offset, typ := extractOffsetAndType(err)
       +        if fileType == "" {
       +                fileType = typ
       +        }
       +
       +        if offset >= 0 {
       +                pos.Offset = offset
       +        }
       +
       +        // The error type from the minifier contains line number and column number.
       +        if line, col := exctractLineNumberAndColumnNumber(err); line >= 0 {
       +                pos.LineNumber = line
       +                pos.ColumnNumber = col
       +                return fileType, pos
       +        }
       +
       +        // Look in the error message for the line number.
       +        for _, handle := range lineNumberExtractors {
       +                lno, col := handle(err)
       +                if lno > 0 {
       +                        pos.ColumnNumber = col
       +                        pos.LineNumber = lno
       +                        break
       +                }
       +        }
       +
       +        return fileType, pos
        }
        
        // UnwrapFileError tries to unwrap a FileError from err.
       @@ -77,49 +235,26 @@ func UnwrapFileError(err error) FileError {
                        switch v := err.(type) {
                        case FileError:
                                return v
       -                case causer:
       -                        err = v.Cause()
                        default:
       -                        return nil
       +                        err = errors.Unwrap(err)
                        }
                }
                return nil
        }
        
       -// ToFileErrorWithOffset will return a new FileError with a line number
       -// with the given offset from the original.
       -func ToFileErrorWithOffset(fe FileError, offset int) FileError {
       -        pos := fe.Position()
       -        return ToFileErrorWithLineNumber(fe, pos.LineNumber+offset)
       -}
       -
       -// ToFileErrorWithOffset will return a new FileError with the given line number.
       -func ToFileErrorWithLineNumber(fe FileError, lineNumber int) FileError {
       -        pos := fe.Position()
       -        pos.LineNumber = lineNumber
       -        return &fileError{cause: fe, fileType: fe.Type(), position: pos}
       -}
       -
       -// ToFileError will convert the given error to an error supporting
       -// the FileError interface.
       -func ToFileError(fileType string, err error) FileError {
       -        for _, handle := range lineNumberExtractors {
       -                lno, col := handle(err)
       -                offset, typ := extractOffsetAndType(err)
       -                if fileType == "" {
       -                        fileType = typ
       -                }
       -
       -                if lno > 0 || offset != -1 {
       -                        return NewFileError(fileType, offset, lno, col, err)
       +// UnwrapFileErrorsWithErrorContext tries to unwrap all FileError in err that has an ErrorContext.
       +func UnwrapFileErrorsWithErrorContext(err error) []FileError {
       +        var errs []FileError
       +        for err != nil {
       +                if v, ok := err.(FileError); ok && v.ErrorContext() != nil {
       +                        errs = append(errs, v)
                        }
       +                err = errors.Unwrap(err)
                }
       -        // Fall back to the pointing to line number 1.
       -        return NewFileError(fileType, -1, 1, 1, err)
       +        return errs
        }
        
        func extractOffsetAndType(e error) (int, string) {
       -        e = errors.Cause(e)
                switch v := e.(type) {
                case *json.UnmarshalTypeError:
                        return int(v.Offset), "json"
       @@ -129,3 +264,15 @@ func extractOffsetAndType(e error) (int, string) {
                        return -1, ""
                }
        }
       +
       +func exctractLineNumberAndColumnNumber(e error) (int, int) {
       +        switch v := e.(type) {
       +        case *parse.Error:
       +                return v.Line, v.Column
       +        case *toml.DecodeError:
       +                return v.Position()
       +
       +        }
       +
       +        return -1, -1
       +}
 (DIR) diff --git a/common/herrors/file_error_test.go b/common/herrors/file_error_test.go
       @@ -1,4 +1,4 @@
       -// Copyright 2018 The Hugo Authors. All rights reserved.
       +// Copyright 2022 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.
       @@ -14,14 +14,44 @@
        package herrors
        
        import (
       +        "fmt"
       +        "strings"
                "testing"
        
       -        "github.com/pkg/errors"
       +        "errors"
       +
       +        "github.com/gohugoio/hugo/common/text"
        
                qt "github.com/frankban/quicktest"
        )
        
       -func TestToLineNumberError(t *testing.T) {
       +func TestNewFileError(t *testing.T) {
       +        t.Parallel()
       +
       +        c := qt.New(t)
       +
       +        fe := NewFileError("foo.html", errors.New("bar"))
       +        c.Assert(fe.Error(), qt.Equals, `"foo.html:1:1": bar`)
       +
       +        lines := ""
       +        for i := 1; i <= 100; i++ {
       +                lines += fmt.Sprintf("line %d\n", i)
       +        }
       +
       +        fe.UpdatePosition(text.Position{LineNumber: 32, ColumnNumber: 2})
       +        c.Assert(fe.Error(), qt.Equals, `"foo.html:32:2": bar`)
       +        fe.UpdatePosition(text.Position{LineNumber: 0, ColumnNumber: 0, Offset: 212})
       +        fe.UpdateContent(strings.NewReader(lines), SimpleLineMatcher)
       +        c.Assert(fe.Error(), qt.Equals, `"foo.html:32:0": bar`)
       +        errorContext := fe.ErrorContext()
       +        c.Assert(errorContext, qt.IsNotNil)
       +        c.Assert(errorContext.Lines, qt.DeepEquals, []string{"line 30", "line 31", "line 32", "line 33", "line 34"})
       +        c.Assert(errorContext.LinesPos, qt.Equals, 2)
       +        c.Assert(errorContext.ChromaLexer, qt.Equals, "go-html-template")
       +
       +}
       +
       +func TestNewFileErrorExtractFromMessage(t *testing.T) {
                t.Parallel()
        
                c := qt.New(t)
       @@ -37,18 +67,16 @@ func TestToLineNumberError(t *testing.T) {
                        {errors.New("parse failed: template: _default/bundle-resource-meta.html:11: unexpected in operand"), 0, 11, 1},
                        {errors.New(`failed:: template: _default/bundle-resource-meta.html:2:7: executing "main" at <.Titles>`), 0, 2, 7},
                        {errors.New(`failed to load translations: (6, 7): was expecting token =, but got "g" instead`), 0, 6, 7},
       +                {errors.New(`execute of template failed: template: index.html:2:5: executing "index.html" at <partial "foo.html" .>: error calling partial: "/layouts/partials/foo.html:3:6": execute of template failed: template: partials/foo.html:3:6: executing "partials/foo.html" at <.ThisDoesNotExist>: can't evaluate field ThisDoesNotExist in type *hugolib.pageStat`), 0, 2, 5},
                } {
        
       -                got := ToFileError("template", test.in)
       +                got := NewFileError("test.txt", test.in)
        
                        errMsg := qt.Commentf("[%d][%T]", i, got)
       -                le, ok := got.(FileError)
       -                c.Assert(ok, qt.Equals, true)
        
       -                c.Assert(ok, qt.Equals, true, errMsg)
       -                pos := le.Position()
       +                pos := got.Position()
                        c.Assert(pos.LineNumber, qt.Equals, test.lineNumber, errMsg)
                        c.Assert(pos.ColumnNumber, qt.Equals, test.columnNumber, errMsg)
       -                c.Assert(errors.Cause(got), qt.Not(qt.IsNil))
       +                c.Assert(errors.Unwrap(got), qt.Not(qt.IsNil))
                }
        }
 (DIR) diff --git a/common/herrors/line_number_extractors.go b/common/herrors/line_number_extractors.go
       @@ -16,36 +16,22 @@ package herrors
        import (
                "regexp"
                "strconv"
       -
       -        "github.com/pkg/errors"
       -
       -        "github.com/pelletier/go-toml/v2"
        )
        
        var lineNumberExtractors = []lineNumberExtractor{
                // Template/shortcode parse errors
       -        newLineNumberErrHandlerFromRegexp(".*:(\\d+):(\\d*):"),
       -        newLineNumberErrHandlerFromRegexp(".*:(\\d+):"),
       +        newLineNumberErrHandlerFromRegexp(`:(\d+):(\d*):`),
       +        newLineNumberErrHandlerFromRegexp(`:(\d+):`),
        
       -        // TOML parse errors
       -        tomlLineNumberExtractor,
                // YAML parse errors
       -        newLineNumberErrHandlerFromRegexp("line (\\d+):"),
       +        newLineNumberErrHandlerFromRegexp(`line (\d+):`),
        
                // i18n bundle errors
       -        newLineNumberErrHandlerFromRegexp("\\((\\d+),\\s(\\d*)"),
       +        newLineNumberErrHandlerFromRegexp(`\((\d+),\s(\d*)`),
        }
        
        type lineNumberExtractor func(e error) (int, int)
        
       -var tomlLineNumberExtractor = func(e error) (int, int) {
       -        e = errors.Cause(e)
       -        if terr, ok := e.(*toml.DecodeError); ok {
       -                return terr.Position()
       -        }
       -        return -1, -1
       -}
       -
        func newLineNumberErrHandlerFromRegexp(expression string) lineNumberExtractor {
                re := regexp.MustCompile(expression)
                return extractLineNo(re)
       @@ -72,6 +58,6 @@ func extractLineNo(re *regexp.Regexp) lineNumberExtractor {
                                return lno, col
                        }
        
       -                return -1, col
       +                return 0, col
                }
        }
 (DIR) diff --git a/common/hugio/copy.go b/common/hugio/copy.go
       @@ -14,13 +14,12 @@
        package hugio
        
        import (
       +        "fmt"
                "io"
                "io/ioutil"
                "os"
                "path/filepath"
        
       -        "github.com/pkg/errors"
       -
                "github.com/spf13/afero"
        )
        
       @@ -60,7 +59,7 @@ func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool
                }
        
                if !fi.IsDir() {
       -                return errors.Errorf("%q is not a directory", from)
       +                return fmt.Errorf("%q is not a directory", from)
                }
        
                err = fs.MkdirAll(to, 0777) // before umask
 (DIR) diff --git a/config/commonConfig.go b/config/commonConfig.go
       @@ -14,12 +14,11 @@
        package config
        
        import (
       +        "fmt"
                "sort"
                "strings"
                "sync"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/common/types"
        
                "github.com/gobwas/glob"
       @@ -207,7 +206,7 @@ func DecodeServer(cfg Provider) (*Server, error) {
                                // There are some tricky infinite loop situations when dealing
                                // when the target does not have a trailing slash.
                                // This can certainly be handled better, but not time for that now.
       -                        return nil, errors.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To)
       +                        return nil, fmt.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To)
                        }
                        s.Redirects[i] = redir
                }
 (DIR) diff --git a/config/configLoader.go b/config/configLoader.go
       @@ -14,14 +14,13 @@
        package config
        
        import (
       +        "fmt"
                "os"
                "path/filepath"
                "strings"
        
                "github.com/gohugoio/hugo/common/herrors"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/common/paths"
        
                "github.com/gohugoio/hugo/common/maps"
       @@ -60,7 +59,7 @@ func FromConfigString(config, configType string) (Provider, error) {
        func FromFile(fs afero.Fs, filename string) (Provider, error) {
                m, err := loadConfigFromFile(fs, filename)
                if err != nil {
       -                return nil, herrors.WithFileContextForFileDefault(err, filename, fs)
       +                return nil, herrors.NewFileErrorFromFile(err, filename, filename, fs, herrors.SimpleLineMatcher)
                }
                return NewFrom(m), nil
        }
       @@ -132,7 +131,7 @@ func LoadConfigFromDir(sourceFs afero.Fs, configDir, environment string) (Provid
                                if err != nil {
                                        // This will be used in error reporting, use the most specific value.
                                        dirnames = []string{path}
       -                                return errors.Wrapf(err, "failed to unmarshl config for path %q", path)
       +                                return fmt.Errorf("failed to unmarshl config for path %q: %w", path, err)
                                }
        
                                var keyPath []string
 (DIR) diff --git a/create/content.go b/create/content.go
       @@ -27,7 +27,7 @@ import (
                "github.com/gohugoio/hugo/common/hexec"
                "github.com/gohugoio/hugo/common/paths"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/hugofs/files"
        
       @@ -94,11 +94,11 @@ func NewContent(h *hugolib.HugoSites, kind, targetPath string) error {
                        }
        
                        if ext == "" {
       -                        return "", errors.Errorf("failed to resolve %q to a archetype template", targetPath)
       +                        return "", fmt.Errorf("failed to resolve %q to a archetype template", targetPath)
                        }
        
                        if !files.IsContentFile(b.targetPath) {
       -                        return "", errors.Errorf("target path %q is not a known content format", b.targetPath)
       +                        return "", fmt.Errorf("target path %q is not a known content format", b.targetPath)
                        }
        
                        return b.buildFile()
       @@ -188,14 +188,14 @@ func (b *contentBuilder) buildDir() error {
        
                        in, err := meta.Open()
                        if err != nil {
       -                        return errors.Wrap(err, "failed to open non-content file")
       +                        return fmt.Errorf("failed to open non-content file: %w", err)
                        }
        
                        targetFilename := filepath.Join(baseDir, b.targetPath, strings.TrimPrefix(filename, b.archetypeFilename))
                        targetDir := filepath.Dir(targetFilename)
        
                        if err := b.sourceFs.MkdirAll(targetDir, 0o777); err != nil && !os.IsExist(err) {
       -                        return errors.Wrapf(err, "failed to create target directory for %q", targetDir)
       +                        return fmt.Errorf("failed to create target directory for %q: %w", targetDir, err)
                        }
        
                        out, err := b.sourceFs.Create(targetFilename)
       @@ -329,7 +329,7 @@ func (b *contentBuilder) mapArcheTypeDir() error {
                w := hugofs.NewWalkway(walkCfg)
        
                if err := w.Walk(); err != nil {
       -                return errors.Wrapf(err, "failed to walk archetype dir %q", b.archetypeFilename)
       +                return fmt.Errorf("failed to walk archetype dir %q: %w", b.archetypeFilename, err)
                }
        
                b.dirMap = m
       @@ -374,7 +374,7 @@ func (b *contentBuilder) usesSiteVar(filename string) (bool, error) {
                }
                bb, err := afero.ReadFile(b.archeTypeFs, filename)
                if err != nil {
       -                return false, errors.Wrap(err, "failed to open archetype file")
       +                return false, fmt.Errorf("failed to open archetype file: %w", err)
                }
        
                return bytes.Contains(bb, []byte(".Site")) || bytes.Contains(bb, []byte("site.")), nil
 (DIR) diff --git a/deploy/deploy.go b/deploy/deploy.go
       @@ -34,11 +34,12 @@ import (
                "strings"
                "sync"
        
       +        "errors"
       +
                "github.com/dustin/go-humanize"
                "github.com/gobwas/glob"
                "github.com/gohugoio/hugo/config"
                "github.com/gohugoio/hugo/media"
       -        "github.com/pkg/errors"
                "github.com/spf13/afero"
                jww "github.com/spf13/jwalterweatherman"
                "golang.org/x/text/unicode/norm"
 (DIR) diff --git a/deploy/deployConfig.go b/deploy/deployConfig.go
       @@ -20,12 +20,13 @@ import (
                "fmt"
                "regexp"
        
       +        "errors"
       +
                "github.com/gobwas/glob"
                "github.com/gohugoio/hugo/config"
                hglob "github.com/gohugoio/hugo/hugofs/glob"
                "github.com/gohugoio/hugo/media"
                "github.com/mitchellh/mapstructure"
       -        "github.com/pkg/errors"
        )
        
        const deploymentConfigKey = "deployment"
 (DIR) diff --git a/deps/deps.go b/deps/deps.go
       @@ -1,12 +1,11 @@
        package deps
        
        import (
       +        "fmt"
                "sync"
                "sync/atomic"
                "time"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/cache/filecache"
                "github.com/gohugoio/hugo/common/hexec"
                "github.com/gohugoio/hugo/common/loggers"
       @@ -186,11 +185,11 @@ func (d *Deps) SetTextTmpl(tmpl tpl.TemplateParseFinder) {
        func (d *Deps) LoadResources() error {
                // Note that the translations need to be loaded before the templates.
                if err := d.translationProvider.Update(d); err != nil {
       -                return errors.Wrap(err, "loading translations")
       +                return fmt.Errorf("loading translations: %w", err)
                }
        
                if err := d.templateProvider.Update(d); err != nil {
       -                return errors.Wrap(err, "loading templates")
       +                return fmt.Errorf("loading templates: %w", err)
                }
        
                return nil
       @@ -236,18 +235,18 @@ func New(cfg DepsCfg) (*Deps, error) {
        
                securityConfig, err := security.DecodeConfig(cfg.Cfg)
                if err != nil {
       -                return nil, errors.WithMessage(err, "failed to create security config from configuration")
       +                return nil, fmt.Errorf("failed to create security config from configuration: %w", err)
                }
                execHelper := hexec.New(securityConfig)
        
                ps, err := helpers.NewPathSpec(fs, cfg.Language, logger)
                if err != nil {
       -                return nil, errors.Wrap(err, "create PathSpec")
       +                return nil, fmt.Errorf("create PathSpec: %w", err)
                }
        
                fileCaches, err := filecache.NewCaches(ps)
                if err != nil {
       -                return nil, errors.WithMessage(err, "failed to create file caches from configuration")
       +                return nil, fmt.Errorf("failed to create file caches from configuration: %w", err)
                }
        
                errorHandler := &globalErrHandler{}
 (DIR) diff --git a/helpers/path.go b/helpers/path.go
       @@ -31,7 +31,6 @@ import (
                "github.com/gohugoio/hugo/hugofs"
        
                "github.com/gohugoio/hugo/common/hugio"
       -        _errors "github.com/pkg/errors"
                "github.com/spf13/afero"
        )
        
       @@ -403,7 +402,7 @@ func GetCacheDir(fs afero.Fs, cfg config.Provider) (string, error) {
                        if !exists {
                                err := fs.MkdirAll(cacheDir, 0777) // Before umask
                                if err != nil {
       -                                return "", _errors.Wrap(err, "failed to create cache dir")
       +                                return "", fmt.Errorf("failed to create cache dir: %w", err)
                                }
                        }
                        return cacheDir, nil
 (DIR) diff --git a/hugofs/decorators.go b/hugofs/decorators.go
       @@ -14,12 +14,11 @@
        package hugofs
        
        import (
       +        "fmt"
                "os"
                "path/filepath"
                "strings"
        
       -        "github.com/pkg/errors"
       -
                "github.com/spf13/afero"
        )
        
       @@ -232,7 +231,7 @@ func (l *baseFileDecoratorFile) Readdir(c int) (ofi []os.FileInfo, err error) {
                        }
                        fi, err = l.fs.decorate(fi, filename)
                        if err != nil {
       -                        return nil, errors.Wrap(err, "decorate")
       +                        return nil, fmt.Errorf("decorate: %w", err)
                        }
                        fisp = append(fisp, fi)
                }
 (DIR) diff --git a/hugofs/fileinfo.go b/hugofs/fileinfo.go
       @@ -28,7 +28,7 @@ import (
                "github.com/gohugoio/hugo/hugofs/files"
                "golang.org/x/text/unicode/norm"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/common/hreflect"
        
 (DIR) diff --git a/hugofs/rootmapping_fs.go b/hugofs/rootmapping_fs.go
       @@ -21,8 +21,6 @@ import (
        
                "github.com/gohugoio/hugo/hugofs/files"
        
       -        "github.com/pkg/errors"
       -
                radix "github.com/armon/go-radix"
                "github.com/spf13/afero"
        )
       @@ -191,7 +189,7 @@ func (fs *RootMappingFs) Dirs(base string) ([]FileMetaInfo, error) {
                        fs = decorateDirs(fs, r.Meta)
                        fi, err := fs.Stat("")
                        if err != nil {
       -                        return nil, errors.Wrap(err, "RootMappingFs.Dirs")
       +                        return nil, fmt.Errorf("RootMappingFs.Dirs: %w", err)
                        }
        
                        if !fi.IsDir() {
       @@ -560,7 +558,7 @@ func (fs *RootMappingFs) doLstat(name string) ([]FileMetaInfo, error) {
        
                if fileCount > 1 {
                        // Not supported by this filesystem.
       -                return nil, errors.Errorf("found multiple files with name %q, use .Readdir or the source filesystem directly", name)
       +                return nil, fmt.Errorf("found multiple files with name %q, use .Readdir or the source filesystem directly", name)
                }
        
                return []FileMetaInfo{roots[0].fi}, nil
 (DIR) diff --git a/hugofs/slice_fs.go b/hugofs/slice_fs.go
       @@ -14,11 +14,12 @@
        package hugofs
        
        import (
       +        "fmt"
                "os"
                "syscall"
                "time"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/spf13/afero"
        )
       @@ -83,7 +84,7 @@ func (fs *SliceFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
                        return decorateFileInfo(fi, fs, fs.getOpener(name), "", "", nil), false, nil
                }
        
       -        return nil, false, errors.Errorf("lstat: files not supported: %q", name)
       +        return nil, false, fmt.Errorf("lstat: files not supported: %q", name)
        }
        
        func (fs *SliceFs) Mkdir(n string, p os.FileMode) error {
 (DIR) diff --git a/hugofs/walk.go b/hugofs/walk.go
       @@ -22,7 +22,7 @@ import (
        
                "github.com/gohugoio/hugo/common/loggers"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/spf13/afero"
        )
       @@ -125,7 +125,7 @@ func (w *Walkway) Walk() error {
                                if w.checkErr(w.root, err) {
                                        return nil
                                }
       -                        return w.walkFn(w.root, nil, errors.Wrapf(err, "walk: %q", w.root))
       +                        return w.walkFn(w.root, nil, fmt.Errorf("walk: %q: %w", w.root, err))
                        }
                        fi = info.(FileMetaInfo)
                }
       @@ -192,7 +192,7 @@ func (w *Walkway) walk(path string, info FileMetaInfo, dirEntries []FileMetaInfo
                                if w.checkErr(path, err) {
                                        return nil
                                }
       -                        return walkFn(path, info, errors.Wrapf(err, "walk: open %q (%q)", path, w.root))
       +                        return walkFn(path, info, fmt.Errorf("walk: open %q (%q): %w", path, w.root, err))
                        }
        
                        fis, err := f.Readdir(-1)
       @@ -201,7 +201,7 @@ func (w *Walkway) walk(path string, info FileMetaInfo, dirEntries []FileMetaInfo
                                if w.checkErr(filename, err) {
                                        return nil
                                }
       -                        return walkFn(path, info, errors.Wrap(err, "walk: Readdir"))
       +                        return walkFn(path, info, fmt.Errorf("walk: Readdir: %w", err))
                        }
        
                        dirEntries = fileInfosToFileMetaInfos(fis)
 (DIR) diff --git a/hugofs/walk_test.go b/hugofs/walk_test.go
       @@ -22,7 +22,7 @@ import (
                "strings"
                "testing"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/common/para"
                "github.com/gohugoio/hugo/htesting"
 (DIR) diff --git a/hugolib/config.go b/hugolib/config.go
       @@ -33,11 +33,12 @@ import (
        
                "github.com/gohugoio/hugo/parser/metadecoders"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/common/herrors"
                "github.com/gohugoio/hugo/common/hugo"
                "github.com/gohugoio/hugo/langs"
                "github.com/gohugoio/hugo/modules"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/config"
                "github.com/gohugoio/hugo/config/privacy"
       @@ -510,5 +511,5 @@ func (configLoader) loadSiteConfig(cfg config.Provider) (scfg SiteConfig, err er
        }
        
        func (l configLoader) wrapFileError(err error, filename string) error {
       -        return herrors.WithFileContextForFileDefault(err, filename, l.Fs)
       +        return herrors.NewFileErrorFromFile(err, filename, filename, l.Fs, herrors.SimpleLineMatcher)
        }
 (DIR) diff --git a/hugolib/configdir_test.go b/hugolib/configdir_test.go
       @@ -146,7 +146,7 @@ baseURL = "https://example.org"
                _, _, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Environment: "development", Filename: "hugo.toml", AbsConfigDir: "config"})
                c.Assert(err, qt.Not(qt.IsNil))
        
       -        fe := herrors.UnwrapErrorWithFileContext(err)
       +        fe := herrors.UnwrapFileError(err)
                c.Assert(fe, qt.Not(qt.IsNil))
                c.Assert(fe.Position().Filename, qt.Equals, filepath.FromSlash("config/development/config.toml"))
        }
 (DIR) diff --git a/hugolib/content_factory.go b/hugolib/content_factory.go
       @@ -14,6 +14,7 @@
        package hugolib
        
        import (
       +        "fmt"
                "io"
                "path/filepath"
                "strings"
       @@ -25,7 +26,6 @@ import (
        
                "github.com/gohugoio/hugo/resources/page"
        
       -        "github.com/pkg/errors"
                "github.com/spf13/afero"
        )
        
       @@ -48,12 +48,12 @@ func (f ContentFactory) ApplyArchetypeFilename(w io.Writer, p page.Page, archety
                }
        
                if fi.IsDir() {
       -                return errors.Errorf("archetype directory (%q) not supported", archetypeFilename)
       +                return fmt.Errorf("archetype directory (%q) not supported", archetypeFilename)
                }
        
                templateSource, err := afero.ReadFile(f.h.SourceFilesystems.Archetypes.Fs, archetypeFilename)
                if err != nil {
       -                return errors.Wrapf(err, "failed to read archetype file %q: %s", archetypeFilename, err)
       +                return fmt.Errorf("failed to read archetype file %q: %s: %w", archetypeFilename, err, err)
        
                }
        
       @@ -79,12 +79,12 @@ func (f ContentFactory) ApplyArchetypeTemplate(w io.Writer, p page.Page, archety
        
                templ, err := ps.s.TextTmpl().Parse("archetype.md", string(templateSource))
                if err != nil {
       -                return errors.Wrapf(err, "failed to parse archetype template: %s", err)
       +                return fmt.Errorf("failed to parse archetype template: %s: %w", err, err)
                }
        
                result, err := executeToString(ps.s.Tmpl(), templ, d)
                if err != nil {
       -                return errors.Wrapf(err, "failed to execute archetype template: %s", err)
       +                return fmt.Errorf("failed to execute archetype template: %s: %w", err, err)
                }
        
                _, err = io.WriteString(w, f.shortcodeReplacerPost.Replace(result))
 (DIR) diff --git a/hugolib/content_map.go b/hugolib/content_map.go
       @@ -23,7 +23,6 @@ import (
                "github.com/gohugoio/hugo/helpers"
        
                "github.com/gohugoio/hugo/resources/page"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/hugofs/files"
        
       @@ -207,7 +206,7 @@ func (b *cmInsertKeyBuilder) WithFile(fi hugofs.FileMetaInfo) *cmInsertKeyBuilde
        
                p, k := b.getBundle(p)
                if k == "" {
       -                b.err = errors.Errorf("no bundle header found for %q", bundlePath)
       +                b.err = fmt.Errorf("no bundle header found for %q", bundlePath)
                        return b
                }
        
 (DIR) diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
       @@ -35,7 +35,6 @@ import (
                "github.com/spf13/cast"
        
                "github.com/gohugoio/hugo/common/para"
       -        "github.com/pkg/errors"
        )
        
        func newPageMaps(h *HugoSites) *pageMaps {
       @@ -131,13 +130,13 @@ func (m *pageMap) newPageFromContentNode(n *contentNode, parentBucket *pagesMapB
        
                gi, err := s.h.gitInfoForPage(ps)
                if err != nil {
       -                return nil, errors.Wrap(err, "failed to load Git data")
       +                return nil, fmt.Errorf("failed to load Git data: %w", err)
                }
                ps.gitInfo = gi
        
                owners, err := s.h.codeownersForPage(ps)
                if err != nil {
       -                return nil, errors.Wrap(err, "failed to load CODEOWNERS")
       +                return nil, fmt.Errorf("failed to load CODEOWNERS: %w", err)
                }
                ps.codeowners = owners
        
       @@ -282,7 +281,7 @@ func (m *pageMap) createSiteTaxonomies() error {
                        } else {
                                taxonomy := m.s.taxonomies[viewName.plural]
                                if taxonomy == nil {
       -                                walkErr = errors.Errorf("missing taxonomy: %s", viewName.plural)
       +                                walkErr = fmt.Errorf("missing taxonomy: %s", viewName.plural)
                                        return true
                                }
                                m.taxonomyEntries.WalkPrefix(s, func(ss string, v any) bool {
 (DIR) diff --git a/hugolib/fileInfo.go b/hugolib/fileInfo.go
       @@ -14,12 +14,11 @@
        package hugolib
        
        import (
       +        "fmt"
                "strings"
        
                "github.com/gohugoio/hugo/hugofs/files"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/hugofs"
        
                "github.com/spf13/afero"
       @@ -41,7 +40,7 @@ type fileInfo struct {
        func (fi *fileInfo) Open() (afero.File, error) {
                f, err := fi.FileInfo().Meta().Open()
                if err != nil {
       -                err = errors.Wrap(err, "fileInfo")
       +                err = fmt.Errorf("fileInfo: %w", err)
                }
        
                return f, err
 (DIR) diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go
       @@ -35,8 +35,6 @@ import (
        
                "github.com/gohugoio/hugo/hugofs/files"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/modules"
        
                hpaths "github.com/gohugoio/hugo/common/paths"
       @@ -176,7 +174,7 @@ func (b *BaseFs) AbsProjectContentDir(filename string) (string, string, error) {
        
                }
        
       -        return "", "", errors.Errorf("could not determine content directory for %q", filename)
       +        return "", "", fmt.Errorf("could not determine content directory for %q", filename)
        }
        
        // ResolveJSConfigFile resolves the JS-related config file to a absolute
       @@ -468,7 +466,7 @@ func NewBase(p *paths.Paths, logger loggers.Logger, options ...func(*BaseFs) err
                builder := newSourceFilesystemsBuilder(p, logger, b)
                sourceFilesystems, err := builder.Build()
                if err != nil {
       -                return nil, errors.Wrap(err, "build filesystems")
       +                return nil, fmt.Errorf("build filesystems: %w", err)
                }
        
                b.SourceFilesystems = sourceFilesystems
       @@ -502,7 +500,7 @@ func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
                if b.theBigFs == nil {
                        theBigFs, err := b.createMainOverlayFs(b.p)
                        if err != nil {
       -                        return nil, errors.Wrap(err, "create main fs")
       +                        return nil, fmt.Errorf("create main fs: %w", err)
                        }
        
                        b.theBigFs = theBigFs
       @@ -544,7 +542,7 @@ func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
        
                contentFs, err := hugofs.NewLanguageFs(b.p.LanguagesDefaultFirst.AsOrdinalSet(), contentBfs)
                if err != nil {
       -                return nil, errors.Wrap(err, "create content filesystem")
       +                return nil, fmt.Errorf("create content filesystem: %w", err)
                }
        
                b.result.Content = b.newSourceFilesystem(files.ComponentFolderContent, contentFs, contentDirs)
 (DIR) diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
       @@ -15,6 +15,7 @@ package hugolib
        
        import (
                "context"
       +        "fmt"
                "io"
                "path/filepath"
                "sort"
       @@ -33,9 +34,10 @@ import (
                "github.com/gohugoio/hugo/output"
                "github.com/gohugoio/hugo/parser/metadecoders"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/common/para"
                "github.com/gohugoio/hugo/hugofs"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/source"
        
       @@ -194,7 +196,7 @@ func (h *hugoSitesInit) Reset() {
        
        func (h *HugoSites) Data() map[string]any {
                if _, err := h.init.data.Do(); err != nil {
       -                h.SendError(errors.Wrap(err, "failed to load data"))
       +                h.SendError(fmt.Errorf("failed to load data: %w", err))
                        return nil
                }
                return h.data
       @@ -242,7 +244,7 @@ func (h *HugoSites) pickOneAndLogTheRest(errors []error) error {
                for j, err := range errors {
                        // If this is in server mode, we want to return an error to the client
                        // with a file context, if possible.
       -                if herrors.UnwrapErrorWithFileContext(err) != nil {
       +                if herrors.UnwrapFileError(err) != nil {
                                i = j
                                break
                        }
       @@ -327,7 +329,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
        
                langConfig, err := newMultiLingualFromSites(cfg.Cfg, sites...)
                if err != nil {
       -                return nil, errors.Wrap(err, "failed to create language config")
       +                return nil, fmt.Errorf("failed to create language config: %w", err)
                }
        
                var contentChangeTracker *contentChangeMap
       @@ -365,7 +367,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
                h.init.data.Add(func() (any, error) {
                        err := h.loadData(h.PathSpec.BaseFs.Data.Dirs)
                        if err != nil {
       -                        return nil, errors.Wrap(err, "failed to load data")
       +                        return nil, fmt.Errorf("failed to load data: %w", err)
                        }
                        return nil, nil
                })
       @@ -391,7 +393,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
                h.init.gitInfo.Add(func() (any, error) {
                        err := h.loadGitInfo()
                        if err != nil {
       -                        return nil, errors.Wrap(err, "failed to load Git info")
       +                        return nil, fmt.Errorf("failed to load Git info: %w", err)
                        }
                        return nil, nil
                })
       @@ -402,7 +404,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
        
                var l configLoader
                if err := l.applyDeps(cfg, sites...); err != nil {
       -                initErr = errors.Wrap(err, "add site dependencies")
       +                initErr = fmt.Errorf("add site dependencies: %w", err)
                }
        
                h.Deps = sites[0].Deps
       @@ -485,7 +487,7 @@ func (l configLoader) applyDeps(cfg deps.DepsCfg, sites ...*Site) error {
        
                                siteConfig, err := l.loadSiteConfig(s.language)
                                if err != nil {
       -                                return errors.Wrap(err, "load site config")
       +                                return fmt.Errorf("load site config: %w", err)
                                }
                                s.siteConfigConfig = siteConfig
        
       @@ -516,17 +518,17 @@ func (l configLoader) applyDeps(cfg deps.DepsCfg, sites ...*Site) error {
                                var err error
                                d, err = deps.New(cfg)
                                if err != nil {
       -                                return errors.Wrap(err, "create deps")
       +                                return fmt.Errorf("create deps: %w", err)
                                }
        
                                d.OutputFormatsConfig = s.outputFormatsConfig
        
                                if err := onCreated(d); err != nil {
       -                                return errors.Wrap(err, "on created")
       +                                return fmt.Errorf("on created: %w", err)
                                }
        
                                if err = d.LoadResources(); err != nil {
       -                                return errors.Wrap(err, "load resources")
       +                                return fmt.Errorf("load resources: %w", err)
                                }
        
                        } else {
       @@ -548,7 +550,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
                }
                sites, err := createSitesFromConfig(cfg)
                if err != nil {
       -                return nil, errors.Wrap(err, "from config")
       +                return nil, fmt.Errorf("from config: %w", err)
                }
                return newHugoSites(cfg, sites...)
        }
       @@ -882,7 +884,7 @@ func (h *HugoSites) handleDataFile(r source.File) error {
        
                f, err := r.FileInfo().Meta().Open()
                if err != nil {
       -                return errors.Wrapf(err, "data: failed to open %q:", r.LogicalName())
       +                return fmt.Errorf("data: failed to open %q: %w", r.LogicalName(), err)
                }
                defer f.Close()
        
       @@ -960,23 +962,16 @@ func (h *HugoSites) errWithFileContext(err error, f source.File) error {
                if !ok {
                        return err
                }
       -
                realFilename := fim.Meta().Filename
        
       -        err, _ = herrors.WithFileContextForFile(
       -                err,
       -                realFilename,
       -                realFilename,
       -                h.SourceSpec.Fs.Source,
       -                herrors.SimpleLineMatcher)
       +        return herrors.NewFileErrorFromFile(err, realFilename, realFilename, h.SourceSpec.Fs.Source, herrors.SimpleLineMatcher)
        
       -        return err
        }
        
        func (h *HugoSites) readData(f source.File) (any, error) {
                file, err := f.FileInfo().Meta().Open()
                if err != nil {
       -                return nil, errors.Wrap(err, "readData: failed to open data file")
       +                return nil, fmt.Errorf("readData: failed to open data file: %w", err)
                }
                defer file.Close()
                content := helpers.ReaderToBytes(file)
 (DIR) diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
       @@ -35,7 +35,7 @@ import (
        
                "github.com/gohugoio/hugo/output"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/fsnotify/fsnotify"
                "github.com/gohugoio/hugo/helpers"
       @@ -50,7 +50,7 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
                if !config.NoBuildLock {
                        unlock, err := h.BaseFs.LockBuild()
                        if err != nil {
       -                        return errors.Wrap(err, "failed to acquire a build lock")
       +                        return fmt.Errorf("failed to acquire a build lock: %w", err)
                        }
                        defer unlock()
                }
       @@ -99,11 +99,11 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
                                        if len(events) > 0 {
                                                // Rebuild
                                                if err := h.initRebuild(conf); err != nil {
       -                                                return errors.Wrap(err, "initRebuild")
       +                                                return fmt.Errorf("initRebuild: %w", err)
                                                }
                                        } else {
                                                if err := h.initSites(conf); err != nil {
       -                                                return errors.Wrap(err, "initSites")
       +                                                return fmt.Errorf("initSites: %w", err)
                                                }
                                        }
        
       @@ -117,7 +117,7 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
                                }
                                trace.WithRegion(ctx, "process", f)
                                if err != nil {
       -                                return errors.Wrap(err, "process")
       +                                return fmt.Errorf("process: %w", err)
                                }
        
                                f = func() {
 (DIR) diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go
       @@ -2,6 +2,7 @@ package hugolib
        
        import (
                "fmt"
       +        "os"
                "path/filepath"
                "strings"
                "testing"
       @@ -17,14 +18,15 @@ type testSiteBuildErrorAsserter struct {
                c    *qt.C
        }
        
       -func (t testSiteBuildErrorAsserter) getFileError(err error) *herrors.ErrorWithFileContext {
       +func (t testSiteBuildErrorAsserter) getFileError(err error) herrors.FileError {
                t.c.Assert(err, qt.Not(qt.IsNil), qt.Commentf(t.name))
       -        ferr := herrors.UnwrapErrorWithFileContext(err)
       -        t.c.Assert(ferr, qt.Not(qt.IsNil))
       -        return ferr
       +        fe := herrors.UnwrapFileError(err)
       +        t.c.Assert(fe, qt.Not(qt.IsNil))
       +        return fe
        }
        
        func (t testSiteBuildErrorAsserter) assertLineNumber(lineNumber int, err error) {
       +        t.c.Helper()
                fe := t.getFileError(err)
                t.c.Assert(fe.Position().LineNumber, qt.Equals, lineNumber, qt.Commentf(err.Error()))
        }
       @@ -87,7 +89,6 @@ func TestSiteBuildErrors(t *testing.T) {
                                        fe := a.getFileError(err)
                                        a.c.Assert(fe.Position().LineNumber, qt.Equals, 5)
                                        a.c.Assert(fe.Position().ColumnNumber, qt.Equals, 1)
       -                                a.c.Assert(fe.ChromaLexer, qt.Equals, "go-html-template")
                                        a.assertErrorMessage("\"layouts/foo/single.html:5:1\": parse failed: template: foo/single.html:5: unexpected \"}\" in operand", fe.Error())
                                },
                        },
       @@ -101,7 +102,6 @@ func TestSiteBuildErrors(t *testing.T) {
                                        fe := a.getFileError(err)
                                        a.c.Assert(fe.Position().LineNumber, qt.Equals, 5)
                                        a.c.Assert(fe.Position().ColumnNumber, qt.Equals, 14)
       -                                a.c.Assert(fe.ChromaLexer, qt.Equals, "go-html-template")
                                        a.assertErrorMessage("\"layouts/_default/single.html:5:14\": execute of template failed", fe.Error())
                                },
                        },
       @@ -115,7 +115,6 @@ func TestSiteBuildErrors(t *testing.T) {
                                        fe := a.getFileError(err)
                                        a.c.Assert(fe.Position().LineNumber, qt.Equals, 5)
                                        a.c.Assert(fe.Position().ColumnNumber, qt.Equals, 14)
       -                                a.c.Assert(fe.ChromaLexer, qt.Equals, "go-html-template")
                                        a.assertErrorMessage("\"layouts/_default/single.html:5:14\": execute of template failed", fe.Error())
                                },
                        },
       @@ -130,18 +129,17 @@ func TestSiteBuildErrors(t *testing.T) {
                                },
                        },
                        {
       -                        name:     "Shortode execute failed",
       +                        name:     "Shortcode execute failed",
                                fileType: shortcode,
                                fileFixer: func(content string) string {
                                        return strings.Replace(content, ".Title", ".Titles", 1)
                                },
                                assertBuildError: func(a testSiteBuildErrorAsserter, err error) {
                                        fe := a.getFileError(err)
       -                                a.c.Assert(fe.Position().LineNumber, qt.Equals, 7)
       -                                a.c.Assert(fe.ChromaLexer, qt.Equals, "md")
                                        // Make sure that it contains both the content file and template
       -                                a.assertErrorMessage(`content/myyaml.md:7:10": failed to render shortcode "sc"`, fe.Error())
       -                                a.assertErrorMessage(`shortcodes/sc.html:4:22: executing "shortcodes/sc.html" at <.Page.Titles>: can't evaluate`, fe.Error())
       +                                a.assertErrorMessage(`"content/myyaml.md:7:10": failed to render shortcode "sc": failed to process shortcode: "layouts/shortcodes/sc.html:4:22": execute of template failed: template: shortcodes/sc.html:4:22: executing "shortcodes/sc.html" at <.Page.Titles>: can't evaluate field Titles in type page.Page`, fe.Error())
       +                                a.c.Assert(fe.Position().LineNumber, qt.Equals, 7)
       +
                                },
                        },
                        {
       @@ -154,7 +152,6 @@ func TestSiteBuildErrors(t *testing.T) {
                                        fe := a.getFileError(err)
                                        a.c.Assert(fe.Position().LineNumber, qt.Equals, 7)
                                        a.c.Assert(fe.Position().ColumnNumber, qt.Equals, 10)
       -                                a.c.Assert(fe.ChromaLexer, qt.Equals, "md")
                                        a.assertErrorMessage(`"content/myyaml.md:7:10": failed to extract shortcode: template for shortcode "nono" not found`, fe.Error())
                                },
                        },
       @@ -162,10 +159,14 @@ func TestSiteBuildErrors(t *testing.T) {
                                name:     "Invalid YAML front matter",
                                fileType: yamlcontent,
                                fileFixer: func(content string) string {
       -                                return strings.Replace(content, "title:", "title: %foo", 1)
       +                                return `---
       +title: "My YAML Content"
       +foo bar
       +---
       +`
                                },
                                assertBuildError: func(a testSiteBuildErrorAsserter, err error) {
       -                                a.assertLineNumber(2, err)
       +                                a.assertLineNumber(3, err)
                                },
                        },
                        {
       @@ -177,7 +178,6 @@ func TestSiteBuildErrors(t *testing.T) {
                                assertBuildError: func(a testSiteBuildErrorAsserter, err error) {
                                        fe := a.getFileError(err)
                                        a.c.Assert(fe.Position().LineNumber, qt.Equals, 6)
       -                                a.c.Assert(fe.ErrorContext.ChromaLexer, qt.Equals, "toml")
                                },
                        },
                        {
       @@ -188,9 +188,7 @@ func TestSiteBuildErrors(t *testing.T) {
                                },
                                assertBuildError: func(a testSiteBuildErrorAsserter, err error) {
                                        fe := a.getFileError(err)
       -
                                        a.c.Assert(fe.Position().LineNumber, qt.Equals, 3)
       -                                a.c.Assert(fe.ErrorContext.ChromaLexer, qt.Equals, "json")
                                },
                        },
                        {
       @@ -211,6 +209,9 @@ func TestSiteBuildErrors(t *testing.T) {
                }
        
                for _, test := range tests {
       +                if test.name != "Invalid JSON front matter" {
       +                        continue
       +                }
                        test := test
                        t.Run(test.name, func(t *testing.T) {
                                t.Parallel()
       @@ -311,6 +312,77 @@ Some content.
                                }
                        })
                }
       +
       +}
       +
       +// Issue 9852
       +func TestErrorMinify(t *testing.T) {
       +        t.Parallel()
       +
       +        files := `
       +-- config.toml --
       +minify = true
       +
       +-- layouts/index.html --
       +<body>
       +<script>=;</script>
       +</body>
       +
       +`
       +
       +        b, err := NewIntegrationTestBuilder(
       +                IntegrationTestConfig{
       +                        T:           t,
       +                        TxtarString: files,
       +                },
       +        ).BuildE()
       +
       +        fe := herrors.UnwrapFileError(err)
       +        b.Assert(fe, qt.IsNotNil)
       +        b.Assert(fe.Position().LineNumber, qt.Equals, 2)
       +        b.Assert(fe.Position().ColumnNumber, qt.Equals, 9)
       +        b.Assert(fe.Error(), qt.Contains, "unexpected = in expression on line 2 and column 9")
       +        b.Assert(filepath.ToSlash(fe.Position().Filename), qt.Contains, "hugo-transform-error")
       +        b.Assert(os.Remove(fe.Position().Filename), qt.IsNil)
       +
       +}
       +
       +func TestErrorNested(t *testing.T) {
       +        t.Parallel()
       +
       +        files := `
       +-- config.toml --
       +-- layouts/index.html --
       +line 1
       +12{{ partial "foo.html" . }}
       +line 4
       +line 5
       +-- layouts/partials/foo.html --
       +line 1
       +line 2
       +123{{ .ThisDoesNotExist }}
       +line 4
       +`
       +
       +        b, err := NewIntegrationTestBuilder(
       +                IntegrationTestConfig{
       +                        T:           t,
       +                        TxtarString: files,
       +                },
       +        ).BuildE()
       +
       +        b.Assert(err, qt.IsNotNil)
       +        errors := herrors.UnwrapFileErrorsWithErrorContext(err)
       +        b.Assert(errors, qt.HasLen, 2)
       +        fmt.Println(errors[0])
       +        b.Assert(errors[0].Position().LineNumber, qt.Equals, 2)
       +        b.Assert(errors[0].Position().ColumnNumber, qt.Equals, 5)
       +        b.Assert(errors[0].Error(), qt.Contains, filepath.FromSlash(`"/layouts/index.html:2:5": execute of template failed`))
       +        b.Assert(errors[0].ErrorContext().Lines, qt.DeepEquals, []string{"line 1", "12{{ partial \"foo.html\" . }}", "line 4", "line 5"})
       +        b.Assert(errors[1].Position().LineNumber, qt.Equals, 3)
       +        b.Assert(errors[1].Position().ColumnNumber, qt.Equals, 6)
       +        b.Assert(errors[1].ErrorContext().Lines, qt.DeepEquals, []string{"line 1", "line 2", "123{{ .ThisDoesNotExist }}", "line 4"})
       +
        }
        
        // https://github.com/gohugoio/hugo/issues/5375
 (DIR) diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
       @@ -169,8 +169,7 @@ func (s *IntegrationTestBuilder) destinationExists(filename string) bool {
        }
        
        func (s *IntegrationTestBuilder) AssertIsFileError(err error) {
       -        var ferr *herrors.ErrorWithFileContext
       -        s.Assert(err, qt.ErrorAs, &ferr)
       +        s.Assert(err, qt.ErrorAs, new(herrors.FileError))
        }
        
        func (s *IntegrationTestBuilder) AssertRenderCountContent(count int) {
 (DIR) diff --git a/hugolib/page.go b/hugolib/page.go
       @@ -39,8 +39,9 @@ import (
                "github.com/gohugoio/hugo/common/herrors"
                "github.com/gohugoio/hugo/parser/metadecoders"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/parser/pageparser"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/output"
        
       @@ -482,7 +483,7 @@ func (p *pageState) renderResources() (err error) {
        
                                src, ok := r.(resource.Source)
                                if !ok {
       -                                err = errors.Errorf("Resource %T does not support resource.Source", src)
       +                                err = fmt.Errorf("Resource %T does not support resource.Source", src)
                                        return
                                }
        
       @@ -560,23 +561,37 @@ func (p *pageState) addDependency(dep identity.Provider) {
        
        // wrapError adds some more context to the given error if possible/needed
        func (p *pageState) wrapError(err error) error {
       -        if _, ok := err.(*herrors.ErrorWithFileContext); ok {
       -                // Preserve the first file context.
       -                return err
       +        if err == nil {
       +                panic("wrapError with nil")
                }
       -        var filename string
       -        if !p.File().IsZero() {
       -                filename = p.File().Filename()
       +
       +        if p.File().IsZero() {
       +                // No more details to add.
       +                return fmt.Errorf("%q: %w", p.Pathc(), err)
                }
        
       -        err, _ = herrors.WithFileContextForFile(
       -                err,
       -                filename,
       -                filename,
       -                p.s.SourceSpec.Fs.Source,
       -                herrors.SimpleLineMatcher)
       +        filename := p.File().Filename()
       +
       +        if ferr := herrors.UnwrapFileError(err); ferr != nil {
       +                errfilename := ferr.Position().Filename
       +                if ferr.ErrorContext() != nil || errfilename == "" || !(errfilename == pageFileErrorName || filepath.IsAbs(errfilename)) {
       +                        return err
       +                }
       +                if filepath.IsAbs(errfilename) {
       +                        filename = errfilename
       +                }
       +                f, ferr2 := p.s.SourceSpec.Fs.Source.Open(filename)
       +                if ferr2 != nil {
       +                        return err
       +                }
       +                defer f.Close()
       +                pos := ferr.Position()
       +                pos.Filename = filename
       +                return ferr.UpdatePosition(pos).UpdateContent(f, herrors.SimpleLineMatcher)
       +        }
       +
       +        return herrors.NewFileErrorFromFile(err, filename, filename, p.s.SourceSpec.Fs.Source, herrors.SimpleLineMatcher)
        
       -        return err
        }
        
        func (p *pageState) getContentConverter() converter.Converter {
       @@ -606,6 +621,9 @@ func (p *pageState) mapContent(bucket *pagesMapBucket, meta *pageMeta) error {
                iter := p.source.parsed.Iterator()
        
                fail := func(err error, i pageparser.Item) error {
       +                if fe, ok := err.(herrors.FileError); ok {
       +                        return fe
       +                }
                        return p.parseError(err, iter.Input(), i.Pos)
                }
        
       @@ -626,7 +644,17 @@ Loop:
                                m, err := metadecoders.Default.UnmarshalToMap(it.Val, f)
                                if err != nil {
                                        if fe, ok := err.(herrors.FileError); ok {
       -                                        return herrors.ToFileErrorWithOffset(fe, iter.LineNumber()-1)
       +                                        // Offset the starting position of front matter.
       +                                        pos := fe.Position()
       +                                        offset := iter.LineNumber() - 1
       +                                        if f == metadecoders.YAML {
       +                                                offset -= 1
       +                                        }
       +                                        pos.LineNumber += offset
       +
       +                                        fe.UpdatePosition(pos)
       +
       +                                        return fe
                                        } else {
                                                return err
                                        }
       @@ -682,7 +710,7 @@ Loop:
        
                                currShortcode, err := s.extractShortcode(ordinal, 0, iter)
                                if err != nil {
       -                                return fail(errors.Wrap(err, "failed to extract shortcode"), it)
       +                                return fail(err, it)
                                }
        
                                currShortcode.pos = it.Pos
       @@ -715,7 +743,7 @@ Loop:
                        case it.IsEOF():
                                break Loop
                        case it.IsError():
       -                        err := fail(errors.WithStack(errors.New(it.ValStr())), it)
       +                        err := fail(errors.New(it.ValStr()), it)
                                currShortcode.err = err
                                return err
        
       @@ -738,17 +766,17 @@ Loop:
        }
        
        func (p *pageState) errorf(err error, format string, a ...any) error {
       -        if herrors.UnwrapErrorWithFileContext(err) != nil {
       +        if herrors.UnwrapFileError(err) != nil {
                        // More isn't always better.
                        return err
                }
                args := append([]any{p.Language().Lang, p.pathOrTitle()}, a...)
       -        format = "[%s] page %q: " + format
       +        args = append(args, err)
       +        format = "[%s] page %q: " + format + ": %w"
                if err == nil {
       -                errors.Errorf(format, args...)
                        return fmt.Errorf(format, args...)
                }
       -        return errors.Wrapf(err, format, args...)
       +        return fmt.Errorf(format, args...)
        }
        
        func (p *pageState) outputFormat() (f output.Format) {
       @@ -759,12 +787,8 @@ func (p *pageState) outputFormat() (f output.Format) {
        }
        
        func (p *pageState) parseError(err error, input []byte, offset int) error {
       -        if herrors.UnwrapFileError(err) != nil {
       -                // Use the most specific location.
       -                return err
       -        }
                pos := p.posFromInput(input, offset)
       -        return herrors.NewFileError("md", -1, pos.LineNumber, pos.ColumnNumber, err)
       +        return herrors.NewFileError("page.md", err).UpdatePosition(pos)
        }
        
        func (p *pageState) pathOrTitle() string {
 (DIR) diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go
       @@ -34,7 +34,6 @@ import (
                "github.com/gohugoio/hugo/related"
        
                "github.com/gohugoio/hugo/source"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/config"
       @@ -765,7 +764,7 @@ func (p *pageMeta) newContentConverter(ps *pageState, markup string, renderingCo
                }
                cp := p.s.ContentSpec.Converters.Get(markup)
                if cp == nil {
       -                return converter.NopConverter, errors.Errorf("no content renderer found for markup %q", p.markup)
       +                return converter.NopConverter, fmt.Errorf("no content renderer found for markup %q", p.markup)
                }
        
                var id string
 (DIR) diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go
       @@ -23,11 +23,12 @@ import (
                "sync"
                "unicode/utf8"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/common/text"
                "github.com/gohugoio/hugo/common/types/hstring"
                "github.com/gohugoio/hugo/identity"
                "github.com/mitchellh/mapstructure"
       -        "github.com/pkg/errors"
                "github.com/spf13/cast"
        
                "github.com/gohugoio/hugo/markup/converter/hooks"
       @@ -348,7 +349,7 @@ func (p *pageContentOutput) RenderString(args ...any) (template.HTML, error) {
                        }
        
                        if err := mapstructure.WeakDecode(m, &opts); err != nil {
       -                        return "", errors.WithMessage(err, "failed to decode options")
       +                        return "", fmt.Errorf("failed to decode options: %w", err)
                        }
                }
        
       @@ -416,7 +417,7 @@ func (p *pageContentOutput) Render(layout ...string) (template.HTML, error) {
                // Make sure to send the *pageState and not the *pageContentOutput to the template.
                res, err := executeToString(p.p.s.Tmpl(), templ, p.p)
                if err != nil {
       -                return "", p.p.wrapError(errors.Wrapf(err, "failed to execute template %q v", layout))
       +                return "", p.p.wrapError(fmt.Errorf("failed to execute template %q v: %w", layout, err))
                }
                return template.HTML(res), nil
        }
 (DIR) diff --git a/hugolib/page__ref.go b/hugolib/page__ref.go
       @@ -19,7 +19,6 @@ import (
                "github.com/gohugoio/hugo/common/text"
        
                "github.com/mitchellh/mapstructure"
       -        "github.com/pkg/errors"
        )
        
        func newPageRef(p *pageState) pageRef {
       @@ -77,7 +76,7 @@ func (p pageRef) decodeRefArgs(args map[string]any) (refArgs, *Site, error) {
        func (p pageRef) ref(argsm map[string]any, source any) (string, error) {
                args, s, err := p.decodeRefArgs(argsm)
                if err != nil {
       -                return "", errors.Wrap(err, "invalid arguments to Ref")
       +                return "", fmt.Errorf("invalid arguments to Ref: %w", err)
                }
        
                if s == nil {
       @@ -94,7 +93,7 @@ func (p pageRef) ref(argsm map[string]any, source any) (string, error) {
        func (p pageRef) relRef(argsm map[string]any, source any) (string, error) {
                args, s, err := p.decodeRefArgs(argsm)
                if err != nil {
       -                return "", errors.Wrap(err, "invalid arguments to Ref")
       +                return "", fmt.Errorf("invalid arguments to Ref: %w", err)
                }
        
                if s == nil {
 (DIR) diff --git a/hugolib/page_unwrap.go b/hugolib/page_unwrap.go
       @@ -14,7 +14,7 @@
        package hugolib
        
        import (
       -        "github.com/pkg/errors"
       +        "fmt"
        
                "github.com/gohugoio/hugo/resources/page"
        )
       @@ -36,7 +36,7 @@ func unwrapPage(in any) (page.Page, error) {
                case nil:
                        return nil, nil
                default:
       -                return nil, errors.Errorf("unwrapPage: %T not supported", in)
       +                return nil, fmt.Errorf("unwrapPage: %T not supported", in)
                }
        }
        
 (DIR) diff --git a/hugolib/pages_process.go b/hugolib/pages_process.go
       @@ -22,7 +22,6 @@ import (
                "github.com/gohugoio/hugo/source"
        
                "github.com/gohugoio/hugo/hugofs/files"
       -        "github.com/pkg/errors"
                "golang.org/x/sync/errgroup"
        
                "github.com/gohugoio/hugo/common/herrors"
       @@ -156,7 +155,7 @@ func (p *sitePagesProcessor) copyFile(fim hugofs.FileMetaInfo) error {
                meta := fim.Meta()
                f, err := meta.Open()
                if err != nil {
       -                return errors.Wrap(err, "copyFile: failed to open")
       +                return fmt.Errorf("copyFile: failed to open: %w", err)
                }
        
                s := p.m.s
 (DIR) diff --git a/hugolib/paths/paths.go b/hugolib/paths/paths.go
       @@ -23,7 +23,6 @@ import (
                "github.com/gohugoio/hugo/config"
                "github.com/gohugoio/hugo/langs"
                "github.com/gohugoio/hugo/modules"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/hugofs"
        )
       @@ -83,7 +82,7 @@ func New(fs *hugofs.Fs, cfg config.Provider) (*Paths, error) {
                baseURLstr := cfg.GetString("baseURL")
                baseURL, err := newBaseURLFromString(baseURLstr)
                if err != nil {
       -                return nil, errors.Wrapf(err, "Failed to create baseURL from %q:", baseURLstr)
       +                return nil, fmt.Errorf("Failed to create baseURL from %q:: %w", baseURLstr, err)
                }
        
                contentDir := filepath.Clean(cfg.GetString("contentDir"))
 (DIR) diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go
       @@ -27,8 +27,9 @@ import (
        
                "github.com/gohugoio/hugo/helpers"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/common/herrors"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/parser/pageparser"
                "github.com/gohugoio/hugo/resources/page"
       @@ -269,6 +270,7 @@ const (
                innerNewlineRegexp = "\n"
                innerCleanupRegexp = `\A<p>(.*)</p>\n\z`
                innerCleanupExpand = "$1"
       +        pageFileErrorName  = "page.md"
        )
        
        func renderShortcode(
       @@ -297,9 +299,10 @@ func renderShortcode(
                                var err error
                                tmpl, err = s.TextTmpl().Parse(templName, templStr)
                                if err != nil {
       -                                fe := herrors.ToFileError("html", err)
       -                                l1, l2 := p.posOffset(sc.pos).LineNumber, fe.Position().LineNumber
       -                                fe = herrors.ToFileErrorWithLineNumber(fe, l1+l2-1)
       +                                fe := herrors.NewFileError(pageFileErrorName, err)
       +                                pos := fe.Position()
       +                                pos.LineNumber += p.posOffset(sc.pos).LineNumber
       +                                fe = fe.UpdatePosition(pos)
                                        return "", false, p.wrapError(fe)
                                }
        
       @@ -308,7 +311,7 @@ func renderShortcode(
                                var found bool
                                tmpl, found = s.TextTmpl().Lookup(templName)
                                if !found {
       -                                return "", false, errors.Errorf("no earlier definition of shortcode %q found", sc.name)
       +                                return "", false, fmt.Errorf("no earlier definition of shortcode %q found", sc.name)
                                }
                        }
                } else {
       @@ -389,9 +392,10 @@ func renderShortcode(
                result, err := renderShortcodeWithPage(s.Tmpl(), tmpl, data)
        
                if err != nil && sc.isInline {
       -                fe := herrors.ToFileError("html", err)
       -                l1, l2 := p.posFromPage(sc.pos).LineNumber, fe.Position().LineNumber
       -                fe = herrors.ToFileErrorWithLineNumber(fe, l1+l2-1)
       +                fe := herrors.NewFileError("shortcode.md", err)
       +                pos := fe.Position()
       +                pos.LineNumber += p.posOffset(sc.pos).LineNumber
       +                fe = fe.UpdatePosition(pos)
                        return "", false, fe
                }
        
       @@ -415,7 +419,7 @@ func (s *shortcodeHandler) renderShortcodesForPage(p *pageState, f output.Format
                for _, v := range s.shortcodes {
                        s, more, err := renderShortcode(0, s.s, tplVariants, v, nil, p)
                        if err != nil {
       -                        err = p.parseError(errors.Wrapf(err, "failed to render shortcode %q", v.name), p.source.parsed.Input(), v.pos)
       +                        err = p.parseError(fmt.Errorf("failed to render shortcode %q: %w", v.name, err), p.source.parsed.Input(), v.pos)
                                return nil, false, err
                        }
                        hasVariants = hasVariants || more
       @@ -447,9 +451,10 @@ func (s *shortcodeHandler) extractShortcode(ordinal, level int, pt *pageparser.I
                cnt := 0
                nestedOrdinal := 0
                nextLevel := level + 1
       +        const errorPrefix = "failed to extract shortcode"
        
                fail := func(err error, i pageparser.Item) error {
       -                return s.parseError(err, pt.Input(), i.Pos)
       +                return s.parseError(fmt.Errorf("%s: %w", errorPrefix, err), pt.Input(), i.Pos)
                }
        
        Loop:
       @@ -508,7 +513,7 @@ Loop:
                                                        // return that error, more specific
                                                        continue
                                                }
       -                                        return sc, fail(errors.Errorf("shortcode %q has no .Inner, yet a closing tag was provided", next.Val), next)
       +                                        return sc, fail(fmt.Errorf("shortcode %q has no .Inner, yet a closing tag was provided", next.Val), next)
                                        }
                                }
                                if next.IsRightShortcodeDelim() {
       @@ -538,7 +543,7 @@ Loop:
                                // Used to check if the template expects inner content.
                                templs := s.s.Tmpl().LookupVariants(sc.name)
                                if templs == nil {
       -                                return nil, errors.Errorf("template for shortcode %q not found", sc.name)
       +                                return nil, fmt.Errorf("%s: template for shortcode %q not found", errorPrefix, sc.name)
                                }
        
                                sc.info = templs[0].(tpl.Info)
       @@ -639,7 +644,7 @@ func renderShortcodeWithPage(h tpl.TemplateHandler, tmpl tpl.Template, data *Sho
        
                err := h.Execute(tmpl, buffer, data)
                if err != nil {
       -                return "", errors.Wrap(err, "failed to process shortcode")
       +                return "", fmt.Errorf("failed to process shortcode: %w", err)
                }
                return buffer.String(), nil
        }
 (DIR) diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go
       @@ -1340,7 +1340,7 @@ func TestShortcodeNoInner(t *testing.T) {
        
                b := newTestSitesBuilder(t)
        
       -        b.WithContent("page.md", `---
       +        b.WithContent("mypage.md", `---
        title: "No Inner!"
        ---
        {{< noinner >}}{{< /noinner >}}
       @@ -1350,7 +1350,7 @@ title: "No Inner!"
                        "layouts/shortcodes/noinner.html", `No inner here.`)
        
                err := b.BuildE(BuildCfg{})
       -        b.Assert(err.Error(), qt.Contains, `failed to extract shortcode: shortcode "noinner" has no .Inner, yet a closing tag was provided`)
       +        b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`"content/mypage.md:4:21": failed to extract shortcode: shortcode "noinner" has no .Inner, yet a closing tag was provided`))
        }
        
        func TestShortcodeStableOutputFormatTemplates(t *testing.T) {
 (DIR) diff --git a/hugolib/site.go b/hugolib/site.go
       @@ -59,8 +59,6 @@ import (
        
                "github.com/gohugoio/hugo/common/hugo"
                "github.com/gohugoio/hugo/publisher"
       -        "github.com/pkg/errors"
       -        _errors "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/langs"
        
       @@ -508,7 +506,7 @@ But this also means that your site configuration may not do what you expect. If 
                if cfg.Language.IsSet("related") {
                        relatedContentConfig, err = related.DecodeConfig(cfg.Language.GetParams("related"))
                        if err != nil {
       -                        return nil, errors.Wrap(err, "failed to decode related config")
       +                        return nil, fmt.Errorf("failed to decode related config: %w", err)
                        }
                } else {
                        relatedContentConfig = related.DefaultConfig
       @@ -546,7 +544,7 @@ But this also means that your site configuration may not do what you expect. If 
                        var err error
                        cascade, err := page.DecodeCascade(cfg.Language.Get("cascade"))
                        if err != nil {
       -                        return nil, errors.Errorf("failed to decode cascade config: %s", err)
       +                        return nil, fmt.Errorf("failed to decode cascade config: %s", err)
                        }
        
                        siteBucket = &pagesMapBucket{
       @@ -1211,11 +1209,11 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
        
        func (s *Site) process(config BuildCfg) (err error) {
                if err = s.initialize(); err != nil {
       -                err = errors.Wrap(err, "initialize")
       +                err = fmt.Errorf("initialize: %w", err)
                        return
                }
                if err = s.readAndProcessContent(config); err != nil {
       -                err = errors.Wrap(err, "readAndProcessContent")
       +                err = fmt.Errorf("readAndProcessContent: %w", err)
                        return
                }
                return err
       @@ -1534,7 +1532,7 @@ func (s *Site) assembleMenus() {
        
                        for name, me := range p.pageMenus.menus() {
                                if _, ok := flat[twoD{name, me.KeyName()}]; ok {
       -                                err := p.wrapError(errors.Errorf("duplicate menu entry with identifier %q in menu %q", me.KeyName(), name))
       +                                err := p.wrapError(fmt.Errorf("duplicate menu entry with identifier %q in menu %q", me.KeyName(), name))
                                        s.Log.Warnln(err)
                                        continue
                                }
       @@ -1819,7 +1817,7 @@ func (s *Site) renderForTemplate(name, outputFormat string, d any, w io.Writer, 
                }
        
                if err = s.Tmpl().Execute(templ, w, d); err != nil {
       -                return _errors.Wrapf(err, "render of %q failed", name)
       +                return fmt.Errorf("render of %q failed: %w", name, err)
                }
                return
        }
 (DIR) diff --git a/hugolib/site_render.go b/hugolib/site_render.go
       @@ -23,8 +23,9 @@ import (
        
                "github.com/gohugoio/hugo/config"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/output"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/resources/page"
                "github.com/gohugoio/hugo/resources/page/pagemeta"
       @@ -95,7 +96,7 @@ func (s *Site) renderPages(ctx *siteRenderContext) error {
        
                err := <-errs
                if err != nil {
       -                return errors.Wrap(err, "failed to render pages")
       +                return fmt.Errorf("failed to render pages: %w", err)
                }
                return nil
        }
 (DIR) diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
       @@ -28,10 +28,8 @@ import (
                "github.com/google/go-cmp/cmp"
        
                "github.com/gohugoio/hugo/parser"
       -        "github.com/pkg/errors"
        
                "github.com/fsnotify/fsnotify"
       -        "github.com/gohugoio/hugo/common/herrors"
                "github.com/gohugoio/hugo/common/hexec"
                "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/config"
       @@ -471,7 +469,6 @@ func (s *sitesBuilder) writeFilePairs(folder string, files []filenameContent) *s
        
        func (s *sitesBuilder) CreateSites() *sitesBuilder {
                if err := s.CreateSitesE(); err != nil {
       -                herrors.PrintStackTraceFromErr(err)
                        s.Fatalf("Failed to create sites: %s", err)
                }
        
       @@ -517,7 +514,7 @@ func (s *sitesBuilder) CreateSitesE() error {
                                        "i18n",
                                } {
                                        if err := os.MkdirAll(filepath.Join(s.workingDir, dir), 0777); err != nil {
       -                                        return errors.Wrapf(err, "failed to create %q", dir)
       +                                        return fmt.Errorf("failed to create %q: %w", dir, err)
                                        }
                                }
                        }
       @@ -536,7 +533,7 @@ func (s *sitesBuilder) CreateSitesE() error {
                }
        
                if err := s.LoadConfig(); err != nil {
       -                return errors.Wrap(err, "failed to load config")
       +                return fmt.Errorf("failed to load config: %w", err)
                }
        
                s.Fs.PublishDir = hugofs.NewCreateCountingFs(s.Fs.PublishDir)
       @@ -549,7 +546,7 @@ func (s *sitesBuilder) CreateSitesE() error {
        
                sites, err := NewHugoSites(depsCfg)
                if err != nil {
       -                return errors.Wrap(err, "failed to create sites")
       +                return fmt.Errorf("failed to create sites: %w", err)
                }
                s.H = sites
        
       @@ -612,7 +609,6 @@ func (s *sitesBuilder) build(cfg BuildCfg, shouldFail bool) *sitesBuilder {
                        }
                }
                if err != nil && !shouldFail {
       -                herrors.PrintStackTraceFromErr(err)
                        s.Fatalf("Build failed: %s", err)
                } else if err == nil && shouldFail {
                        s.Fatalf("Expected error")
 (DIR) diff --git a/langs/config.go b/langs/config.go
       @@ -23,7 +23,7 @@ import (
        
                "github.com/spf13/cast"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/config"
        )
       @@ -72,7 +72,7 @@ func LoadLanguageSettings(cfg config.Provider, oldLangs Languages) (c LanguagesC
                } else {
                        languages2, err = toSortedLanguages(cfg, languages)
                        if err != nil {
       -                        return c, errors.Wrap(err, "Failed to parse multilingual config")
       +                        return c, fmt.Errorf("Failed to parse multilingual config: %w", err)
                        }
                }
        
 (DIR) diff --git a/langs/i18n/translationProvider.go b/langs/i18n/translationProvider.go
       @@ -15,6 +15,7 @@ package i18n
        
        import (
                "encoding/json"
       +        "fmt"
                "strings"
        
                "github.com/gohugoio/hugo/common/paths"
       @@ -30,7 +31,6 @@ import (
                "github.com/gohugoio/hugo/deps"
                "github.com/gohugoio/hugo/hugofs"
                "github.com/gohugoio/hugo/source"
       -        _errors "github.com/pkg/errors"
        )
        
        // TranslationProvider provides translation handling, i.e. loading
       @@ -83,7 +83,7 @@ const artificialLangTagPrefix = "art-x-"
        func addTranslationFile(bundle *i18n.Bundle, r source.File) error {
                f, err := r.FileInfo().Meta().Open()
                if err != nil {
       -                return _errors.Wrapf(err, "failed to open translations file %q:", r.LogicalName())
       +                return fmt.Errorf("failed to open translations file %q:: %w", r.LogicalName(), err)
                }
        
                b := helpers.ReaderToBytes(f)
       @@ -96,7 +96,7 @@ func addTranslationFile(bundle *i18n.Bundle, r source.File) error {
                        try := artificialLangTagPrefix + lang
                        _, err = language.Parse(try)
                        if err != nil {
       -                        return _errors.Errorf("%q %s.", try, err)
       +                        return fmt.Errorf("%q: %s", try, err)
                        }
                        name = artificialLangTagPrefix + name
                }
       @@ -111,7 +111,7 @@ func addTranslationFile(bundle *i18n.Bundle, r source.File) error {
                                        return nil
                                }
                        }
       -                return errWithFileContext(_errors.Wrapf(err, "failed to load translations"), r)
       +                return errWithFileContext(fmt.Errorf("failed to load translations: %w", err), r)
                }
        
                return nil
       @@ -138,11 +138,6 @@ func errWithFileContext(inerr error, r source.File) error {
                }
                defer f.Close()
        
       -        err, _ = herrors.WithFileContext(
       -                inerr,
       -                realFilename,
       -                f,
       -                herrors.SimpleLineMatcher)
       +        return herrors.NewFileError(realFilename, inerr).UpdateContent(f, herrors.SimpleLineMatcher)
        
       -        return err
        }
 (DIR) diff --git a/langs/language.go b/langs/language.go
       @@ -14,6 +14,7 @@
        package langs
        
        import (
       +        "fmt"
                "sort"
                "strings"
                "sync"
       @@ -22,8 +23,6 @@ import (
                "golang.org/x/text/collate"
                "golang.org/x/text/language"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/common/htime"
                "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/config"
       @@ -311,7 +310,7 @@ func GetCollator(l *Language) *Collator {
        func (l *Language) loadLocation(tzStr string) error {
                location, err := time.LoadLocation(tzStr)
                if err != nil {
       -                return errors.Wrapf(err, "invalid timeZone for language %q", l.Lang)
       +                return fmt.Errorf("invalid timeZone for language %q: %w", l.Lang, err)
                }
                l.location = location
        
 (DIR) diff --git a/lazy/init.go b/lazy/init.go
       @@ -19,7 +19,7 @@ import (
                "sync/atomic"
                "time"
        
       -        "github.com/pkg/errors"
       +        "errors"
        )
        
        // New creates a new empty Init.
 (DIR) diff --git a/markup/blackfriday/blackfriday_config/config.go b/markup/blackfriday/blackfriday_config/config.go
       @@ -19,8 +19,9 @@
        package blackfriday_config
        
        import (
       +        "fmt"
       +
                "github.com/mitchellh/mapstructure"
       -        "github.com/pkg/errors"
        )
        
        // Default holds the default BlackFriday config.
       @@ -64,7 +65,7 @@ type Config struct {
        
        func UpdateConfig(b Config, m map[string]any) (Config, error) {
                if err := mapstructure.Decode(m, &b); err != nil {
       -                return b, errors.WithMessage(err, "failed to decode rendering config")
       +                return b, fmt.Errorf("failed to decode rendering config: %w", err)
                }
                return b, nil
        }
 (DIR) diff --git a/modules/client.go b/modules/client.go
       @@ -47,7 +47,8 @@ import (
        
                "github.com/gohugoio/hugo/common/hugio"
        
       -        "github.com/pkg/errors"
       +        "errors"
       +
                "github.com/spf13/afero"
        )
        
       @@ -241,7 +242,7 @@ func (c *Client) Vendor() error {
                        // See https://github.com/gohugoio/hugo/issues/8239
                        // This is an error situation. We need something to vendor.
                        if t.Mounts() == nil {
       -                        return errors.Errorf("cannot vendor module %q, need at least one mount", t.Path())
       +                        return fmt.Errorf("cannot vendor module %q, need at least one mount", t.Path())
                        }
        
                        fmt.Fprintln(&modulesContent, "# "+t.Path()+" "+t.Version())
       @@ -253,22 +254,22 @@ func (c *Client) Vendor() error {
                                targetFilename := filepath.Join(vendorDir, t.Path(), mount.Source)
                                fi, err := c.fs.Stat(sourceFilename)
                                if err != nil {
       -                                return errors.Wrap(err, "failed to vendor module")
       +                                return fmt.Errorf("failed to vendor module: %w", err)
                                }
        
                                if fi.IsDir() {
                                        if err := hugio.CopyDir(c.fs, sourceFilename, targetFilename, nil); err != nil {
       -                                        return errors.Wrap(err, "failed to copy module to vendor dir")
       +                                        return fmt.Errorf("failed to copy module to vendor dir: %w", err)
                                        }
                                } else {
                                        targetDir := filepath.Dir(targetFilename)
        
                                        if err := c.fs.MkdirAll(targetDir, 0755); err != nil {
       -                                        return errors.Wrap(err, "failed to make target dir")
       +                                        return fmt.Errorf("failed to make target dir: %w", err)
                                        }
        
                                        if err := hugio.CopyFile(c.fs, sourceFilename, targetFilename); err != nil {
       -                                        return errors.Wrap(err, "failed to copy module file to vendor")
       +                                        return fmt.Errorf("failed to copy module file to vendor: %w", err)
                                        }
                                }
                        }
       @@ -278,7 +279,7 @@ func (c *Client) Vendor() error {
                        _, err := c.fs.Stat(resourcesDir)
                        if err == nil {
                                if err := hugio.CopyDir(c.fs, resourcesDir, filepath.Join(vendorDir, t.Path(), files.FolderResources), nil); err != nil {
       -                                return errors.Wrap(err, "failed to copy resources to vendor dir")
       +                                return fmt.Errorf("failed to copy resources to vendor dir: %w", err)
                                }
                        }
        
       @@ -287,7 +288,7 @@ func (c *Client) Vendor() error {
                        _, err = c.fs.Stat(configDir)
                        if err == nil {
                                if err := hugio.CopyDir(c.fs, configDir, filepath.Join(vendorDir, t.Path(), "config"), nil); err != nil {
       -                                return errors.Wrap(err, "failed to copy config dir to vendor dir")
       +                                return fmt.Errorf("failed to copy config dir to vendor dir: %w", err)
                                }
                        }
        
       @@ -361,7 +362,7 @@ func (c *Client) get(args ...string) error {
                        args = append([]string{"-d"}, args...)
                }
                if err := c.runGo(context.Background(), c.logger.Out(), append([]string{"get"}, args...)...); err != nil {
       -                errors.Wrapf(err, "failed to get %q", args)
       +                return fmt.Errorf("failed to get %q: %w", args, err)
                }
                return nil
        }
       @@ -372,7 +373,7 @@ func (c *Client) get(args ...string) error {
        func (c *Client) Init(path string) error {
                err := c.runGo(context.Background(), c.logger.Out(), "mod", "init", path)
                if err != nil {
       -                return errors.Wrap(err, "failed to init modules")
       +                return fmt.Errorf("failed to init modules: %w", err)
                }
        
                c.GoModulesFilename = filepath.Join(c.ccfg.WorkingDir, goModFilename)
       @@ -458,7 +459,7 @@ func (c *Client) listGoMods() (goModules, error) {
                        out := ioutil.Discard
                        err := c.runGo(context.Background(), out, args...)
                        if err != nil {
       -                        return errors.Wrap(err, "failed to download modules")
       +                        return fmt.Errorf("failed to download modules: %w", err)
                        }
                        return nil
                }
       @@ -477,7 +478,7 @@ func (c *Client) listGoMods() (goModules, error) {
                        }
                        err := c.runGo(context.Background(), b, args...)
                        if err != nil {
       -                        return errors.Wrap(err, "failed to list modules")
       +                        return fmt.Errorf("failed to list modules: %w", err)
                        }
        
                        dec := json.NewDecoder(b)
       @@ -487,7 +488,7 @@ func (c *Client) listGoMods() (goModules, error) {
                                        if err == io.EOF {
                                                break
                                        }
       -                                return errors.Wrap(err, "failed to decode modules list")
       +                                return fmt.Errorf("failed to decode modules list: %w", err)
                                }
        
                                if err := handle(m); err != nil {
       @@ -657,7 +658,7 @@ If you then run 'hugo mod graph' it should resolve itself to the most recent ver
        
                        _, ok := err.(*exec.ExitError)
                        if !ok {
       -                        return errors.Errorf("failed to execute 'go %v': %s %T", args, err, err)
       +                        return fmt.Errorf("failed to execute 'go %v': %s %T", args, err, err)
                        }
        
                        // Too old Go version
       @@ -666,7 +667,7 @@ If you then run 'hugo mod graph' it should resolve itself to the most recent ver
                                return nil
                        }
        
       -                return errors.Errorf("go command failed: %s", stderr)
       +                return fmt.Errorf("go command failed: %s", stderr)
        
                }
        
       @@ -706,7 +707,7 @@ func (c *Client) shouldVendor(path string) bool {
        }
        
        func (c *Client) createThemeDirname(modulePath string, isProjectMod bool) (string, error) {
       -        invalid := errors.Errorf("invalid module path %q; must be relative to themesDir when defined outside of the project", modulePath)
       +        invalid := fmt.Errorf("invalid module path %q; must be relative to themesDir when defined outside of the project", modulePath)
        
                modulePath = filepath.Clean(modulePath)
                if filepath.IsAbs(modulePath) {
 (DIR) diff --git a/modules/collect.go b/modules/collect.go
       @@ -36,7 +36,7 @@ import (
        
                "github.com/rogpeppe/go-internal/module"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/config"
                "github.com/spf13/afero"
       @@ -48,7 +48,7 @@ const vendorModulesFilename = "modules.txt"
        
        // IsNotExist returns whether an error means that a module could not be found.
        func IsNotExist(err error) bool {
       -        return errors.Cause(err) == ErrNotExist
       +        return errors.Is(err, os.ErrNotExist)
        }
        
        // CreateProjectModule creates modules from the given config.
       @@ -289,7 +289,7 @@ func (c *collector) add(owner *moduleAdapter, moduleImport Import, disabled bool
                                                return nil, nil
                                        }
                                        if found, _ := afero.Exists(c.fs, moduleDir); !found {
       -                                        c.err = c.wrapModuleNotFound(errors.Errorf(`module %q not found; either add it as a Hugo Module or store it in %q.`, modulePath, c.ccfg.ThemesDir))
       +                                        c.err = c.wrapModuleNotFound(fmt.Errorf(`module %q not found; either add it as a Hugo Module or store it in %q.`, modulePath, c.ccfg.ThemesDir))
                                                return nil, nil
                                        }
                                }
       @@ -297,7 +297,7 @@ func (c *collector) add(owner *moduleAdapter, moduleImport Import, disabled bool
                }
        
                if found, _ := afero.Exists(c.fs, moduleDir); !found {
       -                c.err = c.wrapModuleNotFound(errors.Errorf("%q not found", moduleDir))
       +                c.err = c.wrapModuleNotFound(fmt.Errorf("%q not found", moduleDir))
                        return nil, nil
                }
        
       @@ -557,7 +557,7 @@ func (c *collector) collectModulesTXT(owner Module) error {
                        line = strings.TrimSpace(line)
                        parts := strings.Fields(line)
                        if len(parts) != 2 {
       -                        return errors.Errorf("invalid modules list: %q", filename)
       +                        return fmt.Errorf("invalid modules list: %q", filename)
                        }
                        path := parts[0]
        
       @@ -662,7 +662,7 @@ func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mou
                                targetBase = mnt.Target[0:idxPathSep]
                        }
                        if !files.IsComponentFolder(targetBase) {
       -                        return nil, errors.Errorf("%s: mount target must be one of: %v", errMsg, files.ComponentFolders)
       +                        return nil, fmt.Errorf("%s: mount target must be one of: %v", errMsg, files.ComponentFolders)
                        }
        
                        out = append(out, mnt)
       @@ -672,7 +672,7 @@ func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mou
        }
        
        func (c *collector) wrapModuleNotFound(err error) error {
       -        err = errors.Wrap(ErrNotExist, err.Error())
       +        err = fmt.Errorf(err.Error()+": %w", ErrNotExist)
                if c.GoModulesFilename == "" {
                        return err
                }
       @@ -681,9 +681,9 @@ func (c *collector) wrapModuleNotFound(err error) error {
        
                switch c.goBinaryStatus {
                case goBinaryStatusNotFound:
       -                return errors.Wrap(err, baseMsg+" you need to install Go to use it. See https://golang.org/dl/.")
       +                return fmt.Errorf(baseMsg+" you need to install Go to use it. See https://golang.org/dl/ : %q", err)
                case goBinaryStatusTooOld:
       -                return errors.Wrap(err, baseMsg+" you need to a newer version of Go to use it. See https://golang.org/dl/.")
       +                return fmt.Errorf(baseMsg+" you need to a newer version of Go to use it. See https://golang.org/dl/ : %w", err)
                }
        
                return err
 (DIR) diff --git a/modules/config.go b/modules/config.go
       @@ -18,8 +18,6 @@ import (
                "path/filepath"
                "strings"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/common/hugo"
        
                "github.com/gohugoio/hugo/config"
       @@ -226,7 +224,7 @@ func decodeConfig(cfg config.Provider, pathReplacements map[string]string) (Conf
                                for _, repl := range c.Replacements {
                                        parts := strings.Split(repl, "->")
                                        if len(parts) != 2 {
       -                                        return c, errors.Errorf(`invalid module.replacements: %q; configure replacement pairs on the form "oldpath->newpath" `, repl)
       +                                        return c, fmt.Errorf(`invalid module.replacements: %q; configure replacement pairs on the form "oldpath->newpath" `, repl)
                                        }
        
                                        c.replacementsMap[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
 (DIR) diff --git a/modules/npm/package_builder.go b/modules/npm/package_builder.go
       @@ -24,8 +24,6 @@ import (
        
                "github.com/gohugoio/hugo/hugofs/files"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/hugofs"
                "github.com/spf13/afero"
        
       @@ -57,7 +55,7 @@ func Pack(fs afero.Fs, fis []hugofs.FileMetaInfo) error {
                        if err == nil {
                                // Preserve the original in package.hugo.json.
                                if err = hugio.CopyFile(fs, packageJSONName, files.FilenamePackageHugoJSON); err != nil {
       -                                return errors.Wrap(err, "npm pack: failed to copy package file")
       +                                return fmt.Errorf("npm pack: failed to copy package file: %w", err)
                                }
                        } else {
                                // Create one.
       @@ -83,7 +81,7 @@ func Pack(fs afero.Fs, fis []hugofs.FileMetaInfo) error {
                masterFilename := meta.Filename
                f, err := meta.Open()
                if err != nil {
       -                return errors.Wrap(err, "npm pack: failed to open package file")
       +                return fmt.Errorf("npm pack: failed to open package file: %w", err)
                }
                b = newPackageBuilder(meta.Module, f)
                f.Close()
       @@ -106,14 +104,14 @@ func Pack(fs afero.Fs, fis []hugofs.FileMetaInfo) error {
        
                        f, err := meta.Open()
                        if err != nil {
       -                        return errors.Wrap(err, "npm pack: failed to open package file")
       +                        return fmt.Errorf("npm pack: failed to open package file: %w", err)
                        }
                        b.Add(meta.Module, f)
                        f.Close()
                }
        
                if b.Err() != nil {
       -                return errors.Wrap(b.Err(), "npm pack: failed to build")
       +                return fmt.Errorf("npm pack: failed to build: %w", b.Err())
                }
        
                // Replace the dependencies in the original template with the merged set.
       @@ -136,11 +134,11 @@ func Pack(fs afero.Fs, fis []hugofs.FileMetaInfo) error {
                encoder.SetEscapeHTML(false)
                encoder.SetIndent("", strings.Repeat(" ", 2))
                if err := encoder.Encode(b.originalPackageJSON); err != nil {
       -                return errors.Wrap(err, "npm pack: failed to marshal JSON")
       +                return fmt.Errorf("npm pack: failed to marshal JSON: %w", err)
                }
        
                if err := afero.WriteFile(fs, packageJSONName, packageJSONData.Bytes(), 0666); err != nil {
       -                return errors.Wrap(err, "npm pack: failed to write package.json")
       +                return fmt.Errorf("npm pack: failed to write package.json: %w", err)
                }
        
                return nil
 (DIR) diff --git a/navigation/menu.go b/navigation/menu.go
       @@ -19,8 +19,6 @@ import (
                "sort"
                "strings"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/common/types"
                "github.com/gohugoio/hugo/compare"
       @@ -190,7 +188,7 @@ func (m *MenuEntry) MarshallMap(ime map[string]any) error {
                }
        
                if err != nil {
       -                return errors.Wrapf(err, "failed to marshal menu entry %q", m.KeyName())
       +                return fmt.Errorf("failed to marshal menu entry %q: %w", m.KeyName(), err)
                }
        
                return nil
 (DIR) diff --git a/navigation/pagemenus.go b/navigation/pagemenus.go
       @@ -14,10 +14,11 @@
        package navigation
        
        import (
       +        "fmt"
       +
                "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/common/types"
        
       -        "github.com/pkg/errors"
                "github.com/spf13/cast"
        )
        
       @@ -76,7 +77,7 @@ func PageMenusFromPage(p Page) (PageMenus, error) {
                }
        
                var wrapErr = func(err error) error {
       -                return errors.Wrapf(err, "unable to process menus for page %q", p.Path())
       +                return fmt.Errorf("unable to process menus for page %q: %w", p.Path(), err)
                }
        
                // Could be a structured menu entry
 (DIR) diff --git a/output/outputFormat.go b/output/outputFormat.go
       @@ -20,8 +20,6 @@ import (
                "sort"
                "strings"
        
       -        "github.com/pkg/errors"
       -
                "github.com/mitchellh/mapstructure"
        
                "github.com/gohugoio/hugo/media"
       @@ -364,7 +362,7 @@ func decode(mediaTypes media.Types, input any, output *Format) error {
                                                                }
                                                                dataVal.SetMapIndex(key, reflect.ValueOf(mediaType))
                                                        default:
       -                                                        return nil, errors.Errorf("invalid output format configuration; wrong type for media type, expected string (e.g. text/html), got %T", vvi)
       +                                                        return nil, fmt.Errorf("invalid output format configuration; wrong type for media type, expected string (e.g. text/html), got %T", vvi)
                                                        }
                                                }
                                        }
       @@ -379,7 +377,7 @@ func decode(mediaTypes media.Types, input any, output *Format) error {
                }
        
                if err = decoder.Decode(input); err != nil {
       -                return errors.Wrap(err, "failed to decode output format configuration")
       +                return fmt.Errorf("failed to decode output format configuration: %w", err)
                }
        
                return nil
 (DIR) diff --git a/parser/metadecoders/decoder.go b/parser/metadecoders/decoder.go
       @@ -26,7 +26,6 @@ import (
        
                xml "github.com/clbanning/mxj/v2"
                toml "github.com/pelletier/go-toml/v2"
       -        "github.com/pkg/errors"
                "github.com/spf13/afero"
                "github.com/spf13/cast"
                jww "github.com/spf13/jwalterweatherman"
       @@ -74,7 +73,7 @@ func (d Decoder) UnmarshalToMap(data []byte, f Format) (map[string]any, error) {
        func (d Decoder) UnmarshalFileToMap(fs afero.Fs, filename string) (map[string]any, error) {
                format := FormatFromString(filename)
                if format == "" {
       -                return nil, errors.Errorf("%q is not a valid configuration format", filename)
       +                return nil, fmt.Errorf("%q is not a valid configuration format", filename)
                }
        
                data, err := afero.ReadFile(fs, filename)
       @@ -106,7 +105,7 @@ func (d Decoder) UnmarshalStringTo(data string, typ any) (any, error) {
                case float64:
                        return cast.ToFloat64E(data)
                default:
       -                return nil, errors.Errorf("unmarshal: %T not supported", typ)
       +                return nil, fmt.Errorf("unmarshal: %T not supported", typ)
                }
        }
        
       @@ -144,7 +143,7 @@ func (d Decoder) UnmarshalTo(data []byte, f Format, v any) error {
                        if err == nil {
                                xmlRootName, err := xmlRoot.Root()
                                if err != nil {
       -                                return toFileError(f, errors.Wrap(err, "failed to unmarshal XML"))
       +                                return toFileError(f, data, fmt.Errorf("failed to unmarshal XML: %w", err))
                                }
                                xmlValue = xmlRoot[xmlRootName].(map[string]any)
                        }
       @@ -160,7 +159,7 @@ func (d Decoder) UnmarshalTo(data []byte, f Format, v any) error {
                case YAML:
                        err = yaml.Unmarshal(data, v)
                        if err != nil {
       -                        return toFileError(f, errors.Wrap(err, "failed to unmarshal YAML"))
       +                        return toFileError(f, data, fmt.Errorf("failed to unmarshal YAML: %w", err))
                        }
        
                        // To support boolean keys, the YAML package unmarshals maps to
       @@ -191,14 +190,14 @@ func (d Decoder) UnmarshalTo(data []byte, f Format, v any) error {
                        return d.unmarshalCSV(data, v)
        
                default:
       -                return errors.Errorf("unmarshal of format %q is not supported", f)
       +                return fmt.Errorf("unmarshal of format %q is not supported", f)
                }
        
                if err == nil {
                        return nil
                }
        
       -        return toFileError(f, errors.Wrap(err, "unmarshal failed"))
       +        return toFileError(f, data, fmt.Errorf("unmarshal failed: %w", err))
        }
        
        func (d Decoder) unmarshalCSV(data []byte, v any) error {
       @@ -215,7 +214,7 @@ func (d Decoder) unmarshalCSV(data []byte, v any) error {
                case *any:
                        *v.(*any) = records
                default:
       -                return errors.Errorf("CSV cannot be unmarshaled into %T", v)
       +                return fmt.Errorf("CSV cannot be unmarshaled into %T", v)
        
                }
        
       @@ -260,8 +259,8 @@ func (d Decoder) unmarshalORG(data []byte, v any) error {
                return nil
        }
        
       -func toFileError(f Format, err error) error {
       -        return herrors.ToFileError(string(f), err)
       +func toFileError(f Format, data []byte, err error) error {
       +        return herrors.NewFileError(fmt.Sprintf("_stream.%s", f), err).UpdateContent(bytes.NewReader(data), nil)
        }
        
        // stringifyMapKeys recurses into in and changes all instances of
 (DIR) diff --git a/parser/pageparser/pageparser.go b/parser/pageparser/pageparser.go
       @@ -15,11 +15,11 @@ package pageparser
        
        import (
                "bytes"
       +        "fmt"
                "io"
                "io/ioutil"
        
                "github.com/gohugoio/hugo/parser/metadecoders"
       -        "github.com/pkg/errors"
        )
        
        // Result holds the parse result.
       @@ -102,7 +102,7 @@ func ParseMain(r io.Reader, cfg Config) (Result, error) {
        func parseSection(r io.Reader, cfg Config, start stateFunc) (Result, error) {
                b, err := ioutil.ReadAll(r)
                if err != nil {
       -                return nil, errors.Wrap(err, "failed to read page content")
       +                return nil, fmt.Errorf("failed to read page content: %w", err)
                }
                return parseBytes(b, cfg, start)
        }
 (DIR) diff --git a/publisher/publisher.go b/publisher/publisher.go
       @@ -15,6 +15,7 @@ package publisher
        
        import (
                "errors"
       +        "fmt"
                "io"
                "net/url"
                "sync/atomic"
       @@ -104,7 +105,7 @@ func (p DestinationPublisher) Publish(d Descriptor) error {
                        defer bp.PutBuffer(b)
        
                        if err := transformers.Apply(b, d.Src); err != nil {
       -                        return err
       +                        return fmt.Errorf("failed to process %q: %w", d.TargetPath, err)
                        }
        
                        // This is now what we write to disk.
 (DIR) diff --git a/releaser/releaser.go b/releaser/releaser.go
       @@ -26,8 +26,9 @@ import (
        
                "github.com/gohugoio/hugo/common/hexec"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/common/hugo"
       -        "github.com/pkg/errors"
        )
        
        const commitPrefix = "releaser:"
       @@ -217,7 +218,7 @@ func (r *ReleaseHandler) release(releaseNotesFile string) error {
                cmd.Stderr = os.Stderr
                err := cmd.Run()
                if err != nil {
       -                return errors.Wrap(err, "goreleaser failed")
       +                return fmt.Errorf("goreleaser failed: %w", err)
                }
                return nil
        }
 (DIR) diff --git a/resources/image.go b/resources/image.go
       @@ -38,9 +38,6 @@ import (
        
                "github.com/gohugoio/hugo/resources/resource"
        
       -        "github.com/pkg/errors"
       -        _errors "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/helpers"
                "github.com/gohugoio/hugo/resources/images"
        
       @@ -325,7 +322,7 @@ func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src im
                })
                if err != nil {
                        if i.root != nil && i.root.getFileInfo() != nil {
       -                        return nil, errors.Wrapf(err, "image %q", i.root.getFileInfo().Meta().Filename)
       +                        return nil, fmt.Errorf("image %q: %w", i.root.getFileInfo().Meta().Filename, err)
                        }
                }
                return img, nil
       @@ -345,7 +342,7 @@ func (i *imageResource) decodeImageConfig(action, spec string) (images.ImageConf
        func (i *imageResource) DecodeImage() (image.Image, error) {
                f, err := i.ReadSeekCloser()
                if err != nil {
       -                return nil, _errors.Wrap(err, "failed to open image for decode")
       +                return nil, fmt.Errorf("failed to open image for decode: %w", err)
                }
                defer f.Close()
                img, _, err := image.Decode(f)
 (DIR) diff --git a/resources/images/color.go b/resources/images/color.go
       @@ -15,10 +15,9 @@ package images
        
        import (
                "encoding/hex"
       +        "fmt"
                "image/color"
                "strings"
       -
       -        "github.com/pkg/errors"
        )
        
        // AddColorToPalette adds c as the first color in p if not already there.
       @@ -50,7 +49,7 @@ func hexStringToColor(s string) (color.Color, error) {
                s = strings.TrimPrefix(s, "#")
        
                if len(s) != 3 && len(s) != 6 {
       -                return nil, errors.Errorf("invalid color code: %q", s)
       +                return nil, fmt.Errorf("invalid color code: %q", s)
                }
        
                s = strings.ToLower(s)
 (DIR) diff --git a/resources/images/config.go b/resources/images/config.go
       @@ -22,7 +22,7 @@ import (
                "github.com/gohugoio/hugo/helpers"
                "github.com/gohugoio/hugo/media"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/bep/gowebp/libwebp/webpoptions"
        
       @@ -158,7 +158,7 @@ func DecodeConfig(m map[string]any) (ImagingConfig, error) {
                if i.Cfg.Anchor != "" && i.Cfg.Anchor != smartCropIdentifier {
                        anchor, found := anchorPositions[i.Cfg.Anchor]
                        if !found {
       -                        return i, errors.Errorf("invalid anchor value %q in imaging config", i.Anchor)
       +                        return i, fmt.Errorf("invalid anchor value %q in imaging config", i.Anchor)
                        }
                        i.Anchor = anchor
                } else {
       @@ -263,7 +263,7 @@ func DecodeImageConfig(action, config string, defaults ImagingConfig, sourceForm
                                return c, errors.New("must provide Width or Height")
                        }
                default:
       -                return c, errors.Errorf("BUG: unknown action %q encountered while decoding image configuration", c.Action)
       +                return c, fmt.Errorf("BUG: unknown action %q encountered while decoding image configuration", c.Action)
                }
        
                if c.FilterStr == "" {
 (DIR) diff --git a/resources/images/image.go b/resources/images/image.go
       @@ -34,8 +34,9 @@ import (
                "golang.org/x/image/bmp"
                "golang.org/x/image/tiff"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/common/hugio"
       -        "github.com/pkg/errors"
        )
        
        func NewImage(f Format, proc *ImageProcessor, img image.Image, s Spec) *Image {
       @@ -163,7 +164,7 @@ func (i *Image) initConfig() error {
                })
        
                if err != nil {
       -                return errors.Wrap(err, "failed to load image config")
       +                return fmt.Errorf("failed to load image config: %w", err)
                }
        
                return nil
       @@ -239,7 +240,7 @@ func (p *ImageProcessor) ApplyFiltersFromConfig(src image.Image, conf ImageConfi
                case "fit":
                        filters = append(filters, gift.ResizeToFit(conf.Width, conf.Height, conf.Filter))
                default:
       -                return nil, errors.Errorf("unsupported action: %q", conf.Action)
       +                return nil, fmt.Errorf("unsupported action: %q", conf.Action)
                }
        
                img, err := p.Filter(src, filters...)
 (DIR) diff --git a/resources/page/page_generate/generate_page_wrappers.go b/resources/page/page_generate/generate_page_wrappers.go
       @@ -20,7 +20,7 @@ import (
                "path/filepath"
                "reflect"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/common/maps"
        
       @@ -55,15 +55,15 @@ var (
        
        func Generate(c *codegen.Inspector) error {
                if err := generateMarshalJSON(c); err != nil {
       -                return errors.Wrap(err, "failed to generate JSON marshaler")
       +                return fmt.Errorf("failed to generate JSON marshaler: %w", err)
                }
        
                if err := generateDeprecatedWrappers(c); err != nil {
       -                return errors.Wrap(err, "failed to generate deprecate wrappers")
       +                return fmt.Errorf("failed to generate deprecate wrappers: %w", err)
                }
        
                if err := generateFileIsZeroWrappers(c); err != nil {
       -                return errors.Wrap(err, "failed to generate file wrappers")
       +                return fmt.Errorf("failed to generate file wrappers: %w", err)
                }
        
                return nil
 (DIR) diff --git a/resources/page/page_matcher.go b/resources/page/page_matcher.go
       @@ -14,13 +14,13 @@
        package page
        
        import (
       +        "fmt"
                "path/filepath"
                "strings"
        
                "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/hugofs/glob"
                "github.com/mitchellh/mapstructure"
       -        "github.com/pkg/errors"
        )
        
        // A PageMatcher can be used to match a Page with Glob patterns.
       @@ -132,7 +132,7 @@ func DecodePageMatcher(m any, v *PageMatcher) error {
                                }
                        }
                        if !found {
       -                        return errors.Errorf("%q did not match a valid Page Kind", v.Kind)
       +                        return fmt.Errorf("%q did not match a valid Page Kind", v.Kind)
                        }
                }
        
 (DIR) diff --git a/resources/page/pages_related.go b/resources/page/pages_related.go
       @@ -14,11 +14,11 @@
        package page
        
        import (
       +        "fmt"
                "sync"
        
                "github.com/gohugoio/hugo/common/types"
                "github.com/gohugoio/hugo/related"
       -        "github.com/pkg/errors"
                "github.com/spf13/cast"
        )
        
       @@ -108,7 +108,7 @@ func (p Pages) withInvertedIndex(search func(idx *related.InvertedIndex) ([]rela
        
                d, ok := p[0].(InternalDependencies)
                if !ok {
       -                return nil, errors.Errorf("invalid type %T in related search", p[0])
       +                return nil, fmt.Errorf("invalid type %T in related search", p[0])
                }
        
                cache := d.GetRelatedDocsHandler()
 (DIR) diff --git a/resources/page/permalinks.go b/resources/page/permalinks.go
       @@ -23,7 +23,7 @@ import (
                "strings"
                "time"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/helpers"
        )
 (DIR) diff --git a/resources/resource.go b/resources/resource.go
       @@ -31,7 +31,7 @@ import (
                "github.com/gohugoio/hugo/media"
                "github.com/gohugoio/hugo/source"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/common/hugio"
                "github.com/gohugoio/hugo/common/maps"
       @@ -633,7 +633,7 @@ func (fi *resourceFileInfo) hash() (string, error) {
                        var f hugio.ReadSeekCloser
                        f, err = fi.ReadSeekCloser()
                        if err != nil {
       -                        err = errors.Wrap(err, "failed to open source file")
       +                        err = fmt.Errorf("failed to open source file: %w", err)
                                return
                        }
                        defer f.Close()
 (DIR) diff --git a/resources/resource_factories/create/remote.go b/resources/resource_factories/create/remote.go
       @@ -16,6 +16,7 @@ package create
        import (
                "bufio"
                "bytes"
       +        "fmt"
                "io"
                "io/ioutil"
                "mime"
       @@ -34,7 +35,6 @@ import (
                "github.com/gohugoio/hugo/resources"
                "github.com/gohugoio/hugo/resources/resource"
                "github.com/mitchellh/mapstructure"
       -        "github.com/pkg/errors"
        )
        
        type HTTPError struct {
       @@ -77,7 +77,7 @@ func toHTTPError(err error, res *http.Response) *HTTPError {
        func (c *Client) FromRemote(uri string, optionsm map[string]any) (resource.Resource, error) {
                rURL, err := url.Parse(uri)
                if err != nil {
       -                return nil, errors.Wrapf(err, "failed to parse URL for resource %s", uri)
       +                return nil, fmt.Errorf("failed to parse URL for resource %s: %w", uri, err)
                }
        
                resourceID := calculateResourceID(uri, optionsm)
       @@ -85,7 +85,7 @@ func (c *Client) FromRemote(uri string, optionsm map[string]any) (resource.Resou
                _, httpResponse, err := c.cacheGetResource.GetOrCreate(resourceID, func() (io.ReadCloser, error) {
                        options, err := decodeRemoteOptions(optionsm)
                        if err != nil {
       -                        return nil, errors.Wrapf(err, "failed to decode options for resource %s", uri)
       +                        return nil, fmt.Errorf("failed to decode options for resource %s: %w", uri, err)
                        }
                        if err := c.validateFromRemoteArgs(uri, options); err != nil {
                                return nil, err
       @@ -93,7 +93,7 @@ func (c *Client) FromRemote(uri string, optionsm map[string]any) (resource.Resou
        
                        req, err := http.NewRequest(options.Method, uri, options.BodyReader())
                        if err != nil {
       -                        return nil, errors.Wrapf(err, "failed to create request for resource %s", uri)
       +                        return nil, fmt.Errorf("failed to create request for resource %s: %w", uri, err)
                        }
                        addDefaultHeaders(req)
        
       @@ -113,7 +113,7 @@ func (c *Client) FromRemote(uri string, optionsm map[string]any) (resource.Resou
        
                        if res.StatusCode != http.StatusNotFound {
                                if res.StatusCode < 200 || res.StatusCode > 299 {
       -                                return nil, toHTTPError(errors.Errorf("failed to fetch remote resource: %s", http.StatusText(res.StatusCode)), res)
       +                                return nil, toHTTPError(fmt.Errorf("failed to fetch remote resource: %s", http.StatusText(res.StatusCode)), res)
        
                                }
                        }
       @@ -137,7 +137,7 @@ func (c *Client) FromRemote(uri string, optionsm map[string]any) (resource.Resou
        
                body, err := ioutil.ReadAll(res.Body)
                if err != nil {
       -                return nil, errors.Wrapf(err, "failed to read remote resource %q", uri)
       +                return nil, fmt.Errorf("failed to read remote resource %q: %w", uri, err)
                }
        
                filename := path.Base(rURL.Path)
       @@ -172,7 +172,7 @@ func (c *Client) FromRemote(uri string, optionsm map[string]any) (resource.Resou
                // Now resolve the media type primarily using the content.
                mediaType := media.FromContent(c.rs.MediaTypes, extensionHints, body)
                if mediaType.IsZero() {
       -                return nil, errors.Errorf("failed to resolve media type for remote resource %q", uri)
       +                return nil, fmt.Errorf("failed to resolve media type for remote resource %q", uri)
                }
        
                resourceID = filename[:len(filename)-len(path.Ext(filename))] + "_" + resourceID + mediaType.FirstSuffix.FullSuffix
 (DIR) diff --git a/resources/resource_metadata.go b/resources/resource_metadata.go
       @@ -22,7 +22,6 @@ import (
                "github.com/gohugoio/hugo/media"
                "github.com/gohugoio/hugo/resources/resource"
        
       -        "github.com/pkg/errors"
                "github.com/spf13/cast"
        
                "github.com/gohugoio/hugo/common/maps"
       @@ -85,7 +84,7 @@ func AssignMetadata(metadata []map[string]any, resources ...resource.Resource) e
        
                                glob, err := glob.GetGlob(srcKey)
                                if err != nil {
       -                                return errors.Wrap(err, "failed to match resource with metadata")
       +                                return fmt.Errorf("failed to match resource with metadata: %w", err)
                                }
        
                                match := glob.Match(resourceSrcKey)
 (DIR) diff --git a/resources/resource_transformers/babel/babel.go b/resources/resource_transformers/babel/babel.go
       @@ -15,6 +15,7 @@ package babel
        
        import (
                "bytes"
       +        "fmt"
                "io"
                "io/ioutil"
                "os"
       @@ -34,7 +35,6 @@ import (
                "github.com/gohugoio/hugo/common/herrors"
                "github.com/gohugoio/hugo/resources"
                "github.com/gohugoio/hugo/resources/resource"
       -        "github.com/pkg/errors"
        )
        
        // Options from https://babeljs.io/docs/en/options
       @@ -141,7 +141,7 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
                        configFile = t.rs.BaseFs.ResolveJSConfigFile(configFile)
                        if configFile == "" && t.options.Config != "" {
                                // Only fail if the user specified config file is not found.
       -                        return errors.Errorf("babel config %q not found:", configFile)
       +                        return fmt.Errorf("babel config %q not found:", configFile)
                        }
                }
        
       @@ -203,7 +203,7 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
                        if hexec.IsNotFound(err) {
                                return herrors.ErrFeatureNotAvailable
                        }
       -                return errors.Wrap(err, errBuf.String())
       +                return fmt.Errorf(errBuf.String()+": %w", err)
                }
        
                content, err := ioutil.ReadAll(compileOutput)
 (DIR) diff --git a/resources/resource_transformers/integrity/integrity.go b/resources/resource_transformers/integrity/integrity.go
       @@ -19,14 +19,13 @@ import (
                "crypto/sha512"
                "encoding/base64"
                "encoding/hex"
       +        "fmt"
                "hash"
                "html/template"
                "io"
        
                "github.com/gohugoio/hugo/resources/internal"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/resources"
                "github.com/gohugoio/hugo/resources/resource"
        )
       @@ -92,7 +91,7 @@ func newHash(algo string) (hash.Hash, error) {
                case "sha512":
                        return sha512.New(), nil
                default:
       -                return nil, errors.Errorf("unsupported crypto algo: %q, use either md5, sha256, sha384 or sha512", algo)
       +                return nil, fmt.Errorf("unsupported crypto algo: %q, use either md5, sha256, sha384 or sha512", algo)
                }
        }
        
 (DIR) diff --git a/resources/resource_transformers/js/build.go b/resources/resource_transformers/js/build.go
       @@ -22,13 +22,14 @@ import (
                "regexp"
                "strings"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/spf13/afero"
        
                "github.com/gohugoio/hugo/hugofs"
        
                "github.com/gohugoio/hugo/common/herrors"
       +        "github.com/gohugoio/hugo/common/text"
        
                "github.com/gohugoio/hugo/hugolib/filesystems"
                "github.com/gohugoio/hugo/media"
       @@ -109,13 +110,13 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
                        for i, ext := range opts.Inject {
                                impPath := filepath.FromSlash(ext)
                                if filepath.IsAbs(impPath) {
       -                                return errors.Errorf("inject: absolute paths not supported, must be relative to /assets")
       +                                return fmt.Errorf("inject: absolute paths not supported, must be relative to /assets")
                                }
        
                                m := resolveComponentInAssets(t.c.rs.Assets.Fs, impPath)
        
                                if m == nil {
       -                                return errors.Errorf("inject: file %q not found", ext)
       +                                return fmt.Errorf("inject: file %q not found", ext)
                                }
        
                                opts.Inject[i] = m.Filename
       @@ -157,10 +158,12 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
                                }
        
                                if err == nil {
       -                                fe := herrors.NewFileError("js", 0, loc.Line, loc.Column, errors.New(msg.Text))
       -                                err, _ := herrors.WithFileContext(fe, path, f, herrors.SimpleLineMatcher)
       +                                fe := herrors.NewFileError(path, errors.New(msg.Text)).
       +                                        UpdatePosition(text.Position{Offset: -1, LineNumber: loc.Line, ColumnNumber: loc.Column}).
       +                                        UpdateContent(f, herrors.SimpleLineMatcher)
       +
                                        f.Close()
       -                                return err
       +                                return fe
                                }
        
                                return fmt.Errorf("%s", msg.Text)
 (DIR) diff --git a/resources/resource_transformers/js/options.go b/resources/resource_transformers/js/options.go
       @@ -21,7 +21,6 @@ import (
                "strings"
        
                "github.com/gohugoio/hugo/common/maps"
       -        "github.com/pkg/errors"
                "github.com/spf13/afero"
        
                "github.com/evanw/esbuild/pkg/api"
       @@ -251,7 +250,7 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
                                        func(args api.OnLoadArgs) (api.OnLoadResult, error) {
                                                b, err := ioutil.ReadFile(args.Path)
                                                if err != nil {
       -                                                return api.OnLoadResult{}, errors.Wrapf(err, "failed to read %q", args.Path)
       +                                                return api.OnLoadResult{}, fmt.Errorf("failed to read %q: %w", args.Path, err)
                                                }
                                                c := string(b)
                                                return api.OnLoadResult{
       @@ -274,7 +273,7 @@ func createBuildPlugins(c *Client, opts Options) ([]api.Plugin, error) {
        
                b, err := json.Marshal(params)
                if err != nil {
       -                return nil, errors.Wrap(err, "failed to marshal params")
       +                return nil, fmt.Errorf("failed to marshal params: %w", err)
                }
                bs := string(b)
                paramsPlugin := api.Plugin{
 (DIR) diff --git a/resources/resource_transformers/postcss/postcss.go b/resources/resource_transformers/postcss/postcss.go
       @@ -17,6 +17,7 @@ import (
                "bytes"
                "crypto/sha256"
                "encoding/hex"
       +        "fmt"
                "io"
                "io/ioutil"
                "path"
       @@ -36,8 +37,9 @@ import (
                "github.com/spf13/afero"
                "github.com/spf13/cast"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/hugofs"
       -        "github.com/pkg/errors"
        
                "github.com/mitchellh/mapstructure"
        
       @@ -161,7 +163,7 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
                        configFile = t.rs.BaseFs.ResolveJSConfigFile(configFile)
                        if configFile == "" && t.options.Config != "" {
                                // Only fail if the user specified config file is not found.
       -                        return errors.Errorf("postcss config %q not found:", configFile)
       +                        return fmt.Errorf("postcss config %q not found:", configFile)
                        }
                }
        
       @@ -388,15 +390,9 @@ func (imp *importResolver) toFileError(output string) error {
                if err != nil {
                        return inErr
                }
       -        realFilename := fi.(hugofs.FileMetaInfo).Meta().Filename
       -
       -        ferr := herrors.NewFileError("css", -1, file.Offset+1, 1, inErr)
        
       -        werr, ok := herrors.WithFileContextForFile(ferr, realFilename, file.Filename, imp.fs, herrors.SimpleLineMatcher)
       +        realFilename := fi.(hugofs.FileMetaInfo).Meta().Filename
        
       -        if !ok {
       -                return ferr
       -        }
       +        return herrors.NewFileErrorFromFile(inErr, file.Filename, realFilename, hugofs.Os, herrors.SimpleLineMatcher)
        
       -        return werr
        }
 (DIR) diff --git a/resources/resource_transformers/templates/execute_as_template.go b/resources/resource_transformers/templates/execute_as_template.go
       @@ -15,12 +15,13 @@
        package templates
        
        import (
       +        "fmt"
       +
                "github.com/gohugoio/hugo/helpers"
                "github.com/gohugoio/hugo/resources"
                "github.com/gohugoio/hugo/resources/internal"
                "github.com/gohugoio/hugo/resources/resource"
                "github.com/gohugoio/hugo/tpl"
       -        "github.com/pkg/errors"
        )
        
        // Client contains methods to perform template processing of Resource objects.
       @@ -55,7 +56,7 @@ func (t *executeAsTemplateTransform) Transform(ctx *resources.ResourceTransforma
                tplStr := helpers.ReaderToString(ctx.From)
                templ, err := t.t.TextTmpl().Parse(ctx.InPath, tplStr)
                if err != nil {
       -                return errors.Wrapf(err, "failed to parse Resource %q as Template:", ctx.InPath)
       +                return fmt.Errorf("failed to parse Resource %q as Template:: %w", ctx.InPath, err)
                }
        
                ctx.OutPath = t.targetPath
 (DIR) diff --git a/resources/resource_transformers/tocss/dartsass/transform.go b/resources/resource_transformers/tocss/dartsass/transform.go
       @@ -120,18 +120,8 @@ func (t *transform) Transform(ctx *resources.ResourceTransformationCtx) error {
                                        return m.Offset+len(m.Line) >= start.Offset && strings.Contains(m.Line, context)
                                }
        
       -                        ferr, ok := herrors.WithFileContextForFile(
       -                                herrors.NewFileError("scss", -1, -1, start.Column, sassErr),
       -                                filename,
       -                                filename,
       -                                hugofs.Os,
       -                                offsetMatcher)
       -
       -                        if !ok {
       -                                return sassErr
       -                        }
       +                        return herrors.NewFileErrorFromFile(sassErr, filename, filename, hugofs.Os, offsetMatcher)
        
       -                        return ferr
                        }
                        return err
                }
 (DIR) diff --git a/resources/resource_transformers/tocss/scss/tocss.go b/resources/resource_transformers/tocss/scss/tocss.go
       @@ -28,7 +28,6 @@ import (
                "github.com/gohugoio/hugo/hugofs"
                "github.com/gohugoio/hugo/media"
                "github.com/gohugoio/hugo/resources"
       -        "github.com/pkg/errors"
        )
        
        // Used in tests. This feature requires Hugo to be built with the extended tag.
       @@ -172,7 +171,7 @@ func (c *Client) toCSS(options libsass.Options, dst io.Writer, src io.Reader) (l
                in := helpers.ReaderToString(src)
        
                // See https://github.com/gohugoio/hugo/issues/7059
       -        // We need to preserver the regular CSS imports. This is by far
       +        // We need to preserve the regular CSS imports. This is by far
                // a perfect solution, and only works for the main entry file, but
                // that should cover many use cases, e.g. using SCSS as a preprocessor
                // for Tailwind.
       @@ -181,7 +180,7 @@ func (c *Client) toCSS(options libsass.Options, dst io.Writer, src io.Reader) (l
        
                res, err = transpiler.Execute(in)
                if err != nil {
       -                return res, errors.Wrap(err, "SCSS processing failed")
       +                return res, fmt.Errorf("SCSS processing failed: %w", err)
                }
        
                out := res.CSS
 (DIR) diff --git a/resources/transform.go b/resources/transform.go
       @@ -24,8 +24,6 @@ import (
        
                "github.com/gohugoio/hugo/common/paths"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/resources/images"
                "github.com/gohugoio/hugo/resources/images/exif"
                "github.com/spf13/afero"
       @@ -431,10 +429,10 @@ func (r *resourceAdapter) transform(publish, setContent bool) error {
                                                errMsg = ". You need to install Babel, see https://gohugo.io/hugo-pipes/babel/"
                                        }
        
       -                                return errors.Wrap(err, msg+errMsg)
       +                                return fmt.Errorf(msg+errMsg+": %w", err)
                                }
        
       -                        return errors.Wrap(err, msg)
       +                        return fmt.Errorf(msg+": %w", err)
                        }
        
                        var tryFileCache bool
       @@ -461,7 +459,7 @@ func (r *resourceAdapter) transform(publish, setContent bool) error {
                                        if err != nil {
                                                return newErr(err)
                                        }
       -                                return newErr(errors.Errorf("resource %q not found in file cache", key))
       +                                return newErr(fmt.Errorf("resource %q not found in file cache", key))
                                }
                                transformedContentr = f
                                updates.sourceFs = cache.fileCache.Fs
 (DIR) diff --git a/source/fileInfo.go b/source/fileInfo.go
       @@ -14,6 +14,7 @@
        package source
        
        import (
       +        "fmt"
                "path/filepath"
                "strings"
                "sync"
       @@ -22,8 +23,6 @@ import (
        
                "github.com/gohugoio/hugo/hugofs/files"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/common/hugio"
        
                "github.com/gohugoio/hugo/hugofs"
       @@ -244,11 +243,11 @@ func (sp *SourceSpec) NewFileInfo(fi hugofs.FileMetaInfo) (*FileInfo, error) {
                relPath := m.Path
        
                if relPath == "" {
       -                return nil, errors.Errorf("no Path provided by %v (%T)", m, m.Fs)
       +                return nil, fmt.Errorf("no Path provided by %v (%T)", m, m.Fs)
                }
        
                if filename == "" {
       -                return nil, errors.Errorf("no Filename provided by %v (%T)", m, m.Fs)
       +                return nil, fmt.Errorf("no Filename provided by %v (%T)", m, m.Fs)
                }
        
                relDir := filepath.Dir(relPath)
 (DIR) diff --git a/source/filesystem.go b/source/filesystem.go
       @@ -14,11 +14,10 @@
        package source
        
        import (
       +        "fmt"
                "path/filepath"
                "sync"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/hugofs"
        )
        
       @@ -49,7 +48,7 @@ func (f *Filesystem) Files() ([]File, error) {
                f.filesInit.Do(func() {
                        err := f.captureFiles()
                        if err != nil {
       -                        f.filesInitErr = errors.Wrap(err, "capture files")
       +                        f.filesInitErr = fmt.Errorf("capture files: %w", err)
                        }
                })
                return f.files, f.filesInitErr
 (DIR) diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go
       @@ -24,12 +24,13 @@ import (
                "strings"
                "time"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/common/collections"
                "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/common/types"
                "github.com/gohugoio/hugo/deps"
                "github.com/gohugoio/hugo/helpers"
       -        "github.com/pkg/errors"
                "github.com/spf13/cast"
        )
        
       @@ -736,7 +737,7 @@ func (ns *Namespace) Uniq(seq any) (any, error) {
                case reflect.Array:
                        slice = reflect.MakeSlice(reflect.SliceOf(v.Type().Elem()), 0, 0)
                default:
       -                return nil, errors.Errorf("type %T not supported", seq)
       +                return nil, fmt.Errorf("type %T not supported", seq)
                }
        
                seen := make(map[any]bool)
 (DIR) diff --git a/tpl/collections/merge.go b/tpl/collections/merge.go
       @@ -14,13 +14,14 @@
        package collections
        
        import (
       +        "fmt"
                "reflect"
                "strings"
        
                "github.com/gohugoio/hugo/common/hreflect"
                "github.com/gohugoio/hugo/common/maps"
        
       -        "github.com/pkg/errors"
       +        "errors"
        )
        
        // Merge creates a copy of the final parameter and merges the preceding
       @@ -49,7 +50,7 @@ func (ns *Namespace) merge(src, dst any) (any, error) {
                vdst, vsrc := reflect.ValueOf(dst), reflect.ValueOf(src)
        
                if vdst.Kind() != reflect.Map {
       -                return nil, errors.Errorf("destination must be a map, got %T", dst)
       +                return nil, fmt.Errorf("destination must be a map, got %T", dst)
                }
        
                if !hreflect.IsTruthfulValue(vsrc) {
       @@ -57,11 +58,11 @@ func (ns *Namespace) merge(src, dst any) (any, error) {
                }
        
                if vsrc.Kind() != reflect.Map {
       -                return nil, errors.Errorf("source must be a map, got %T", src)
       +                return nil, fmt.Errorf("source must be a map, got %T", src)
                }
        
                if vsrc.Type().Key() != vdst.Type().Key() {
       -                return nil, errors.Errorf("incompatible map types, got %T to %T", src, dst)
       +                return nil, fmt.Errorf("incompatible map types, got %T to %T", src, dst)
                }
        
                return mergeMap(vdst, vsrc).Interface(), nil
 (DIR) diff --git a/tpl/collections/reflect_helpers.go b/tpl/collections/reflect_helpers.go
       @@ -18,8 +18,9 @@ import (
                "reflect"
                "time"
        
       +        "errors"
       +
                "github.com/mitchellh/hashstructure"
       -        "github.com/pkg/errors"
        )
        
        var (
       @@ -103,7 +104,7 @@ func convertValue(v reflect.Value, to reflect.Type) (reflect.Value, error) {
                case isNumber(kind):
                        return convertNumber(v, kind)
                default:
       -                return reflect.Value{}, errors.Errorf("%s is not assignable to %s", v.Type(), to)
       +                return reflect.Value{}, fmt.Errorf("%s is not assignable to %s", v.Type(), to)
                }
        }
        
 (DIR) diff --git a/tpl/collections/symdiff.go b/tpl/collections/symdiff.go
       @@ -16,8 +16,6 @@ package collections
        import (
                "fmt"
                "reflect"
       -
       -        "github.com/pkg/errors"
        )
        
        // SymDiff returns the symmetric difference of s1 and s2.
       @@ -54,7 +52,7 @@ func (ns *Namespace) SymDiff(s2, s1 any) (any, error) {
                                        if ids1[key] != ids2[key] {
                                                v, err := convertValue(ev, sliceElemType)
                                                if err != nil {
       -                                                return nil, errors.WithMessage(err, "symdiff: failed to convert value")
       +                                                return nil, fmt.Errorf("symdiff: failed to convert value: %w", err)
                                                }
                                                slice = reflect.Append(slice, v)
                                        }
 (DIR) diff --git a/tpl/data/data.go b/tpl/data/data.go
       @@ -20,6 +20,7 @@ import (
                "encoding/csv"
                "encoding/json"
                "errors"
       +        "fmt"
                "net/http"
                "strings"
        
       @@ -35,7 +36,6 @@ import (
        
                "github.com/gohugoio/hugo/cache/filecache"
                "github.com/gohugoio/hugo/deps"
       -        _errors "github.com/pkg/errors"
        )
        
        // New returns a new instance of the data-namespaced template functions.
       @@ -69,7 +69,7 @@ func (ns *Namespace) GetCSV(sep string, args ...any) (d [][]string, err error) {
        
                unmarshal := func(b []byte) (bool, error) {
                        if d, err = parseCSV(b, sep); err != nil {
       -                        err = _errors.Wrapf(err, "failed to parse CSV file %s", url)
       +                        err = fmt.Errorf("failed to parse CSV file %s: %w", url, err)
        
                                return true, err
                        }
       @@ -80,7 +80,7 @@ func (ns *Namespace) GetCSV(sep string, args ...any) (d [][]string, err error) {
                var req *http.Request
                req, err = http.NewRequest("GET", url, nil)
                if err != nil {
       -                return nil, _errors.Wrapf(err, "failed to create request for getCSV for resource %s", url)
       +                return nil, fmt.Errorf("failed to create request for getCSV for resource %s: %w", url, err)
                }
        
                // Add custom user headers.
       @@ -109,7 +109,7 @@ func (ns *Namespace) GetJSON(args ...any) (any, error) {
        
                req, err := http.NewRequest("GET", url, nil)
                if err != nil {
       -                return nil, _errors.Wrapf(err, "Failed to create request for getJSON resource %s", url)
       +                return nil, fmt.Errorf("Failed to create request for getJSON resource %s: %w", url, err)
                }
        
                unmarshal := func(b []byte) (bool, error) {
 (DIR) diff --git a/tpl/data/resources.go b/tpl/data/resources.go
       @@ -15,14 +15,13 @@ package data
        
        import (
                "bytes"
       +        "fmt"
                "io/ioutil"
                "net/http"
                "net/url"
                "path/filepath"
                "time"
        
       -        "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/cache/filecache"
        
                "github.com/gohugoio/hugo/config"
       @@ -70,7 +69,7 @@ func (ns *Namespace) getRemote(cache *filecache.Cache, unmarshal func([]byte) (b
                                res.Body.Close()
        
                                if isHTTPError(res) {
       -                                return nil, errors.Errorf("Failed to retrieve remote file: %s, body: %q", http.StatusText(res.StatusCode), b)
       +                                return nil, fmt.Errorf("Failed to retrieve remote file: %s, body: %q", http.StatusText(res.StatusCode), b)
                                }
        
                                retry, err = unmarshal(b)
 (DIR) diff --git a/tpl/images/images.go b/tpl/images/images.go
       @@ -18,7 +18,7 @@ import (
                "image"
                "sync"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/resources/images"
        
 (DIR) diff --git a/tpl/internal/resourcehelpers/helpers.go b/tpl/internal/resourcehelpers/helpers.go
       @@ -19,8 +19,6 @@ import (
                "errors"
                "fmt"
        
       -        _errors "github.com/pkg/errors"
       -
                "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/resources"
        )
       @@ -64,7 +62,7 @@ func ResolveArgs(args []any) (resources.ResourceTransformer, map[string]any, err
        
                m, err := maps.ToStringMapE(args[0])
                if err != nil {
       -                return nil, nil, _errors.Wrap(err, "invalid options type")
       +                return nil, nil, fmt.Errorf("invalid options type: %w", err)
                }
        
                return r, m, nil
 (DIR) diff --git a/tpl/lang/lang.go b/tpl/lang/lang.go
       @@ -20,9 +20,10 @@ import (
                "strconv"
                "strings"
        
       +        "errors"
       +
                "github.com/gohugoio/locales"
                translators "github.com/gohugoio/localescompressed"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/common/hreflect"
                "github.com/gohugoio/hugo/deps"
       @@ -49,7 +50,7 @@ func (ns *Namespace) Translate(id any, args ...any) (string, error) {
        
                if len(args) > 0 {
                        if len(args) > 1 {
       -                        return "", errors.Errorf("wrong number of arguments, expecting at most 2, got %d", len(args)+1)
       +                        return "", fmt.Errorf("wrong number of arguments, expecting at most 2, got %d", len(args)+1)
                        }
                        templateData = args[0]
                }
 (DIR) diff --git a/tpl/openapi/openapi3/openapi3.go b/tpl/openapi/openapi3/openapi3.go
       @@ -14,11 +14,12 @@
        package openapi3
        
        import (
       +        "fmt"
                "io/ioutil"
        
                gyaml "github.com/ghodss/yaml"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                kopenapi3 "github.com/getkin/kin-openapi/openapi3"
                "github.com/gohugoio/hugo/cache/namedmemcache"
       @@ -57,7 +58,7 @@ func (ns *Namespace) Unmarshal(r resource.UnmarshableResource) (*kopenapi3.T, er
                v, err := ns.cache.GetOrCreate(key, func() (any, error) {
                        f := metadecoders.FormatFromMediaType(r.MediaType())
                        if f == "" {
       -                        return nil, errors.Errorf("MIME %q not supported", r.MediaType())
       +                        return nil, fmt.Errorf("MIME %q not supported", r.MediaType())
                        }
        
                        reader, err := r.ReadSeekCloser()
 (DIR) diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go
       @@ -20,8 +20,9 @@ import (
        
                "github.com/gohugoio/hugo/common/herrors"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/common/maps"
       -        "github.com/pkg/errors"
        
                "github.com/gohugoio/hugo/tpl/internal/resourcehelpers"
        
       @@ -162,7 +163,7 @@ func (ns *Namespace) GetRemote(args ...any) resource.Resource {
                        case *create.HTTPError:
                                return resources.NewErrorResource(resource.NewResourceError(v, v.Data))
                        default:
       -                        return resources.NewErrorResource(resource.NewResourceError(errors.Wrap(err, "error calling resources.GetRemote"), make(map[string]any)))
       +                        return resources.NewErrorResource(resource.NewResourceError(fmt.Errorf("error calling resources.GetRemote: %w", err), make(map[string]any)))
                        }
        
                }
       @@ -354,7 +355,7 @@ func (ns *Namespace) ToCSS(args ...any) (resource.Resource, error) {
                                case transpilerDart, transpilerLibSass:
                                        transpiler = cast.ToString(t)
                                default:
       -                                return nil, errors.Errorf("unsupported transpiler %q; valid values are %q or %q", t, transpilerLibSass, transpilerDart)
       +                                return nil, fmt.Errorf("unsupported transpiler %q; valid values are %q or %q", t, transpilerLibSass, transpilerDart)
                                }
                        }
                }
 (DIR) diff --git a/tpl/strings/strings.go b/tpl/strings/strings.go
       @@ -16,6 +16,7 @@ package strings
        
        import (
                "errors"
       +        "fmt"
                "html/template"
                "regexp"
                "strings"
       @@ -25,7 +26,6 @@ import (
                "github.com/gohugoio/hugo/deps"
                "github.com/gohugoio/hugo/helpers"
        
       -        _errors "github.com/pkg/errors"
                "github.com/spf13/cast"
        )
        
       @@ -48,7 +48,7 @@ type Namespace struct {
        func (ns *Namespace) CountRunes(s any) (int, error) {
                ss, err := cast.ToStringE(s)
                if err != nil {
       -                return 0, _errors.Wrap(err, "Failed to convert content to string")
       +                return 0, fmt.Errorf("Failed to convert content to string: %w", err)
                }
        
                counter := 0
       @@ -65,7 +65,7 @@ func (ns *Namespace) CountRunes(s any) (int, error) {
        func (ns *Namespace) RuneCount(s any) (int, error) {
                ss, err := cast.ToStringE(s)
                if err != nil {
       -                return 0, _errors.Wrap(err, "Failed to convert content to string")
       +                return 0, fmt.Errorf("Failed to convert content to string: %w", err)
                }
                return utf8.RuneCountInString(ss), nil
        }
       @@ -74,12 +74,12 @@ func (ns *Namespace) RuneCount(s any) (int, error) {
        func (ns *Namespace) CountWords(s any) (int, error) {
                ss, err := cast.ToStringE(s)
                if err != nil {
       -                return 0, _errors.Wrap(err, "Failed to convert content to string")
       +                return 0, fmt.Errorf("Failed to convert content to string: %w", err)
                }
        
                isCJKLanguage, err := regexp.MatchString(`\p{Han}|\p{Hangul}|\p{Hiragana}|\p{Katakana}`, ss)
                if err != nil {
       -                return 0, _errors.Wrap(err, "Failed to match regex pattern against string")
       +                return 0, fmt.Errorf("Failed to match regex pattern against string: %w", err)
                }
        
                if !isCJKLanguage {
       @@ -104,11 +104,11 @@ func (ns *Namespace) CountWords(s any) (int, error) {
        func (ns *Namespace) Count(substr, s any) (int, error) {
                substrs, err := cast.ToStringE(substr)
                if err != nil {
       -                return 0, _errors.Wrap(err, "Failed to convert substr to string")
       +                return 0, fmt.Errorf("Failed to convert substr to string: %w", err)
                }
                ss, err := cast.ToStringE(s)
                if err != nil {
       -                return 0, _errors.Wrap(err, "Failed to convert s to string")
       +                return 0, fmt.Errorf("Failed to convert s to string: %w", err)
                }
                return strings.Count(ss, substrs), nil
        }
 (DIR) diff --git a/tpl/tplimpl/embedded/templates/server/error.html b/tpl/tplimpl/embedded/templates/server/error.html
       @@ -0,0 +1,63 @@
       +<!DOCTYPE html>
       +<html class="no-js" lang="">
       +  <head>
       +    <meta charset="utf-8" />
       +    <title>Hugo Server: Error</title>
       +    <style type="text/css">
       +      body {
       +        font-family: "Muli", avenir, -apple-system, BlinkMacSystemFont,
       +          "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji",
       +          "Segoe UI Emoji", "Segoe UI Symbol";
       +        font-size: 16px;
       +        color: #48b685;
       +        background-color: #2f1e2e;
       +      }
       +      main {
       +        margin: auto;
       +        width: 95%;
       +        padding: 1rem;
       +      }
       +      .version {
       +        color: #ccc;
       +        padding: 1rem 0;
       +      }
       +      .stack {
       +        margin-top: 4rem;
       +      }
       +      pre {
       +        white-space: pre-wrap;
       +        white-space: -moz-pre-wrap;
       +        white-space: -pre-wrap;
       +        white-space: -o-pre-wrap;
       +        word-wrap: break-word;
       +      }
       +      .highlight {
       +        overflow-x: auto;
       +        margin-bottom: 1rem;
       +      }
       +      a {
       +        color: #0594cb;
       +        text-decoration: none;
       +      }
       +      a:hover {
       +        color: #ccc;
       +      }
       +    </style>
       +  </head>
       +  <body>
       +    <main>
       +      {{ highlight .Error "apl" "linenos=false,noclasses=true,style=paraiso-dark" }}
       +      {{ range $i, $e := .Files }}
       +        {{ if not .ErrorContext }}
       +          {{ continue }}
       +        {{ end }}
       +        {{ $params := printf "noclasses=true,style=paraiso-dark,linenos=table,hl_lines=%d,linenostart=%d" (add .ErrorContext.LinesPos 1) (sub .Position.LineNumber .ErrorContext.LinesPos) }}
       +        {{ $lexer := .ErrorContext.ChromaLexer | default "go-html-template" }}
       +        <h3><code>{{ path.Base .Position.Filename }}:</code></h3>
       +        {{ highlight (delimit .ErrorContext.Lines "\n") $lexer $params }}
       +      {{ end }}
       +      <p class="version">{{ .Version }}</p>
       +      <a href="">Reload Page</a>
       +    </main>
       +  </body>
       +</html>
 (DIR) diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
       @@ -17,6 +17,7 @@ import (
                "bytes"
                "context"
                "embed"
       +        "fmt"
                "io"
                "io/fs"
                "os"
       @@ -42,7 +43,6 @@ import (
                "github.com/gohugoio/hugo/common/herrors"
                "github.com/gohugoio/hugo/hugofs"
                "github.com/gohugoio/hugo/hugofs/files"
       -        "github.com/pkg/errors"
        
                htmltemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
                texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
       @@ -551,14 +551,10 @@ func (t *templateHandler) addFileContext(templ tpl.Template, inerr error) error 
                        }
                        defer f.Close()
        
       -                fe, ok := herrors.WithFileContext(inErr, info.realFilename, f, lineMatcher)
       -                if ok {
       -                        return fe, true
       -                }
       -                return inErr, false
       +                return herrors.NewFileError(info.realFilename, inErr).UpdateContent(f, lineMatcher), true
                }
        
       -        inerr = errors.Wrap(inerr, "execute of template failed")
       +        inerr = fmt.Errorf("execute of template failed: %w", inerr)
        
                if err, ok := checkFilename(ts.info, inerr); ok {
                        return err
       @@ -736,6 +732,7 @@ func (t *templateHandler) extractIdentifiers(line string) []string {
        
        //go:embed embedded/templates/*
        //go:embed embedded/templates/_default/*
       +//go:embed embedded/templates/server/*
        var embededTemplatesFs embed.FS
        
        func (t *templateHandler) loadEmbedded() error {
       @@ -755,10 +752,10 @@ func (t *templateHandler) loadEmbedded() error {
                        name := strings.TrimPrefix(filepath.ToSlash(path), "embedded/templates/")
                        templateName := name
        
       -                // For the render hooks it does not make sense to preseve the
       +                // For the render hooks and the server templates it does not make sense to preseve the
                        // double _indternal double book-keeping,
                        // just add it if its now provided by the user.
       -                if !strings.Contains(path, "_default/_markup") {
       +                if !strings.Contains(path, "_default/_markup") && !strings.HasPrefix(name, "server/") {
                                templateName = internalPathPrefix + name
                        }
        
 (DIR) diff --git a/tpl/tplimpl/template_ast_transformers.go b/tpl/tplimpl/template_ast_transformers.go
       @@ -14,6 +14,7 @@
        package tplimpl
        
        import (
       +        "fmt"
                "regexp"
                "strings"
        
       @@ -22,10 +23,11 @@ import (
        
                "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/common/maps"
                "github.com/gohugoio/hugo/tpl"
                "github.com/mitchellh/mapstructure"
       -        "github.com/pkg/errors"
        )
        
        type templateType int
       @@ -239,14 +241,14 @@ func (c *templateContext) collectConfig(n *parse.PipeNode) {
                }
        
                if s, ok := cmd.Args[0].(*parse.StringNode); ok {
       -                errMsg := "failed to decode $_hugo_config in template"
       +                errMsg := "failed to decode $_hugo_config in template: %w"
                        m, err := maps.ToStringMapE(s.Text)
                        if err != nil {
       -                        c.err = errors.Wrap(err, errMsg)
       +                        c.err = fmt.Errorf(errMsg, err)
                                return
                        }
                        if err := mapstructure.WeakDecode(m, &c.t.parseInfo.Config); err != nil {
       -                        c.err = errors.Wrap(err, errMsg)
       +                        c.err = fmt.Errorf(errMsg, err)
                        }
                }
        }
 (DIR) diff --git a/tpl/tplimpl/template_errors.go b/tpl/tplimpl/template_errors.go
       @@ -14,8 +14,9 @@
        package tplimpl
        
        import (
       +        "fmt"
       +
                "github.com/gohugoio/hugo/common/herrors"
       -        "github.com/pkg/errors"
                "github.com/spf13/afero"
        )
        
       @@ -51,14 +52,13 @@ func (t templateInfo) resolveType() templateType {
        }
        
        func (info templateInfo) errWithFileContext(what string, err error) error {
       -        err = errors.Wrapf(err, what)
       -
       -        err, _ = herrors.WithFileContextForFile(
       -                err,
       -                info.realFilename,
       -                info.filename,
       -                info.fs,
       -                herrors.SimpleLineMatcher)
       +        err = fmt.Errorf(what+": %w", err)
       +        fe := herrors.NewFileError(info.realFilename, err)
       +        f, err := info.fs.Open(info.filename)
       +        if err != nil {
       +                return err
       +        }
       +        defer f.Close()
       +        return fe.UpdateContent(f, herrors.SimpleLineMatcher)
        
       -        return err
        }
 (DIR) diff --git a/tpl/transform/remarshal.go b/tpl/transform/remarshal.go
       @@ -4,7 +4,7 @@ import (
                "bytes"
                "strings"
        
       -        "github.com/pkg/errors"
       +        "errors"
        
                "github.com/gohugoio/hugo/parser"
                "github.com/gohugoio/hugo/parser/metadecoders"
 (DIR) diff --git a/tpl/transform/unmarshal.go b/tpl/transform/unmarshal.go
       @@ -14,6 +14,7 @@
        package transform
        
        import (
       +        "fmt"
                "io/ioutil"
                "strings"
        
       @@ -23,9 +24,10 @@ import (
        
                "github.com/mitchellh/mapstructure"
        
       +        "errors"
       +
                "github.com/gohugoio/hugo/helpers"
                "github.com/gohugoio/hugo/parser/metadecoders"
       -        "github.com/pkg/errors"
        
                "github.com/spf13/cast"
        )
       @@ -54,7 +56,7 @@ func (ns *Namespace) Unmarshal(args ...any) (any, error) {
                        data = args[1]
                        decoder, err = decodeDecoder(m)
                        if err != nil {
       -                        return nil, errors.WithMessage(err, "failed to decode options")
       +                        return nil, fmt.Errorf("failed to decode options: %w", err)
                        }
                }
        
       @@ -72,7 +74,7 @@ func (ns *Namespace) Unmarshal(args ...any) (any, error) {
                        return ns.cache.GetOrCreate(key, func() (any, error) {
                                f := metadecoders.FormatFromMediaType(r.MediaType())
                                if f == "" {
       -                                return nil, errors.Errorf("MIME %q not supported", r.MediaType())
       +                                return nil, fmt.Errorf("MIME %q not supported", r.MediaType())
                                }
        
                                reader, err := r.ReadSeekCloser()
       @@ -92,7 +94,7 @@ func (ns *Namespace) Unmarshal(args ...any) (any, error) {
        
                dataStr, err := types.ToStringE(data)
                if err != nil {
       -                return nil, errors.Errorf("type %T not supported", data)
       +                return nil, fmt.Errorf("type %T not supported", data)
                }
        
                if dataStr == "" {
       @@ -160,7 +162,7 @@ func stringToRune(v any) (rune, error) {
                        if i == 0 {
                                r = rr
                        } else {
       -                        return 0, errors.Errorf("invalid character: %q", v)
       +                        return 0, fmt.Errorf("invalid character: %q", v)
                        }
                }
        
 (DIR) diff --git a/tpl/urls/urls.go b/tpl/urls/urls.go
       @@ -22,7 +22,6 @@ import (
        
                "github.com/gohugoio/hugo/common/urls"
                "github.com/gohugoio/hugo/deps"
       -        _errors "github.com/pkg/errors"
                "github.com/spf13/cast"
        )
        
       @@ -55,7 +54,7 @@ func (ns *Namespace) AbsURL(s any) (template.HTML, error) {
        func (ns *Namespace) Parse(rawurl any) (*url.URL, error) {
                s, err := cast.ToStringE(rawurl)
                if err != nil {
       -                return nil, _errors.Wrap(err, "Error in Parse")
       +                return nil, fmt.Errorf("Error in Parse: %w", err)
                }
        
                return url.Parse(s)
 (DIR) diff --git a/transform/chain.go b/transform/chain.go
       @@ -16,8 +16,11 @@ package transform
        import (
                "bytes"
                "io"
       +        "io/ioutil"
        
                bp "github.com/gohugoio/hugo/bufferpool"
       +        "github.com/gohugoio/hugo/common/herrors"
       +        "github.com/gohugoio/hugo/hugofs"
        )
        
        // Transformer is the func that needs to be implemented by a transformation step.
       @@ -103,7 +106,17 @@ func (c *Chain) Apply(to io.Writer, from io.Reader) error {
                        }
        
                        if err := tr(fb); err != nil {
       -                        return err
       +                        // Write output to a temp file so it can be read by the user for trouble shooting.
       +                        filename := "output.html"
       +                        tempfile, ferr := ioutil.TempFile("", "hugo-transform-error")
       +                        if ferr == nil {
       +                                filename = tempfile.Name()
       +                                defer tempfile.Close()
       +                                _, _ = io.Copy(tempfile, fb.from)
       +                                return herrors.NewFileErrorFromFile(err, filename, filename, hugofs.Os, nil)
       +                        }
       +                        return herrors.NewFileError(filename, err).UpdateContent(fb.from, nil)
       +
                        }
                }