Repository: gohugoio/hugo (language: go)

Issue:
server errors: Print output lines of all files involved in an error
Currently, when there is a runtime error in, say, a shortcode, you get this:

<img width="859" alt="image" src="https://user-images.githubusercontent.com/394382/167092324-9714d51c-2c76-4b2f-970c-1cf2409f455c.png">

You get correctly pointed to the line in the shortcode, but the output lines below isn't that relevant to the error. It would be great if we also could see the offending line of the shortcode in this output


Discussion / hints:

Fix patch:
diff --git a/cache/filecache/filecache_config.go b/cache/filecache/filecache_config.go
index 816c6b8a6d9..a82133ab7f9 100644
--- 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)
 }
diff --git a/cache/filecache/filecache_pruner.go b/cache/filecache/filecache_pruner.go
index db187570157..e5e5719725d 100644
--- 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)
 		}
 
 	}
diff --git a/commands/commandeer.go b/commands/commandeer.go
index c42de5d11a5..5b192c17250 100644
--- 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
 }
diff --git a/commands/convert.go b/commands/convert.go
index d3cad2bd65c..1ec965a0b18 100644
--- 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
diff --git a/commands/hugo.go b/commands/hugo.go
index 9033fac90cb..6bd0605db65 100644
--- 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 {
diff --git a/commands/new_site.go b/commands/new_site.go
index e49a6020284..384c6365bee 100644
--- 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)
 		}
 	}
 
diff --git a/commands/server.go b/commands/server.go
index 145a889bb8c..9857753368b 100644
--- 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)
diff --git a/commands/server_errors.go b/commands/server_errors.go
index 0e3bc50deae..edf6581560f 100644
--- 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)}
diff --git a/common/herrors/error_locator.go b/common/herrors/error_locator.go
index 2c0d215b163..5d2c10c0403 100644
--- 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
 }
diff --git a/common/herrors/errors.go b/common/herrors/errors.go
index 27cfd26937e..6ce90885353 100644
--- 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)
diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go
index 1cb31ff9f25..abd36cfbca6 100644
--- 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
+}
diff --git a/common/herrors/line_number_extractors.go b/common/herrors/line_number_extractors.go
index 3df62bdfe3a..f70a2691fc3 100644
--- 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
 	}
 }
diff --git a/common/hugio/copy.go b/common/hugio/copy.go
index be4506f4cd1..7c52f872346 100644
--- 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
diff --git a/config/commonConfig.go b/config/commonConfig.go
index f34d1bac845..efaa589d18e 100644
--- 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
 	}
diff --git a/config/configLoader.go b/config/configLoader.go
index 76a7e2635d9..6bbad7002bb 100644
--- 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
diff --git a/create/content.go b/create/content.go
index 9034ba29935..5f05e25f9d9 100644
--- 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
diff --git a/deploy/deploy.go b/deploy/deploy.go
index e33bbf3f2a3..f0a4e017837 100644
--- 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"
diff --git a/deploy/deployConfig.go b/deploy/deployConfig.go
index cdc146f217a..ad251692249 100644
--- 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"
diff --git a/deps/deps.go b/deps/deps.go
index a9e78860de1..ece4203024f 100644
--- 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{}
diff --git a/helpers/path.go b/helpers/path.go
index 73970d5581e..0fb365f43bb 100644
--- 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
diff --git a/hugofs/decorators.go b/hugofs/decorators.go
index be0ae495d39..3762d753b0e 100644
--- 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)
 	}
diff --git a/hugofs/fileinfo.go b/hugofs/fileinfo.go
index 29ac3c240e8..af37fce7e66 100644
--- 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"
 
diff --git a/hugofs/rootmapping_fs.go b/hugofs/rootmapping_fs.go
index a891ba8def9..90df48f8cae 100644
--- 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
diff --git a/hugofs/slice_fs.go b/hugofs/slice_fs.go
index a9a3f1bbcf6..7edaf75137d 100644
--- 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 {
diff --git a/hugofs/walk.go b/hugofs/walk.go
index 26f1b74e038..22a99402f32 100644
--- 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)
diff --git a/hugolib/config.go b/hugolib/config.go
index 5d2c6ddf7a1..b2713758ac2 100644
--- 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)
 }
diff --git a/hugolib/content_factory.go b/hugolib/content_factory.go
index bea98894da6..e6e82979f02 100644
--- 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))
diff --git a/hugolib/content_map.go b/hugolib/content_map.go
index 330391dcb68..6849998b6f5 100644
--- 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
 	}
 
diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
index 21a4e8f2abe..a16e4720d9d 100644
--- 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 {
diff --git a/hugolib/fileInfo.go b/hugolib/fileInfo.go
index fdfd34b168a..1cdd7041d63 100644
--- 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
diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go
index d02f8c624c5..2447246d636 100644
--- 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)
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index d67652dab7e..e0d69a8af48 100644
--- 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)
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index 4616b6dbb59..1a191257c18 100644
--- 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() {
diff --git a/hugolib/page.go b/hugolib/page.go
index 77165c07292..4faefa3ccb3 100644
--- 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 {
diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go
index 0c269d57a64..1c8748869a8 100644
--- 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
diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go
index c77d7c32e12..fdce8e802e6 100644
--- 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
 }
diff --git a/hugolib/page__ref.go b/hugolib/page__ref.go
index 24231797358..e55a8a3e490 100644
--- 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 {
diff --git a/hugolib/page_unwrap.go b/hugolib/page_unwrap.go
index 0e985953b16..c3e1ce8dddf 100644
--- 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)
 	}
 }
 
diff --git a/hugolib/pages_process.go b/hugolib/pages_process.go
index 47687eaad8a..04ac0218ad4 100644
--- 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
diff --git a/hugolib/paths/paths.go b/hugolib/paths/paths.go
index 9d5716e161b..501665676c2 100644
--- 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"))
diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go
index ac9e4a69947..42877b53780 100644
--- 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
 }
diff --git a/hugolib/site.go b/hugolib/site.go
index bbabf91a3a1..cf7f0ff8249 100644
--- 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
 }
diff --git a/hugolib/site_render.go b/hugolib/site_render.go
index c09e5cc997f..b572c443e1b 100644
--- 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
 }
diff --git a/langs/config.go b/langs/config.go
index ef63e700ecf..81e6fc2aba2 100644
--- 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)
 		}
 	}
 
diff --git a/langs/i18n/translationProvider.go b/langs/i18n/translationProvider.go
index 44bd52f0c36..52f9349c27d 100644
--- 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
 }
diff --git a/langs/language.go b/langs/language.go
index a0294a10341..d6b30ec1005 100644
--- 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
 
diff --git a/lazy/init.go b/lazy/init.go
index 48e9e2489a4..b998d0305d3 100644
--- 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.
diff --git a/markup/blackfriday/blackfriday_config/config.go b/markup/blackfriday/blackfriday_config/config.go
index 668478ece9e..8c276c9f6ec 100644
--- 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
 }
diff --git a/modules/client.go b/modules/client.go
index 6e1cfe881c3..78ba9f5aeec 100644
--- 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) {
diff --git a/modules/collect.go b/modules/collect.go
index e012d4696a0..ff83f9ecca8 100644
--- 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
diff --git a/modules/config.go b/modules/config.go
index 04bf1b42206..08154bc110f 100644
--- 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])
diff --git a/modules/npm/package_builder.go b/modules/npm/package_builder.go
index 911241813b3..9bdc7eb78a9 100644
--- 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
diff --git a/navigation/menu.go b/navigation/menu.go
index 02e1f8fec3f..5e4996f3964 100644
--- 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
diff --git a/navigation/pagemenus.go b/navigation/pagemenus.go
index 46ed5221e6a..7b4f6f6486c 100644
--- 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
diff --git a/output/outputFormat.go b/output/outputFormat.go
index 34bec991185..1f1556eafe4 100644
--- 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
diff --git a/parser/metadecoders/decoder.go b/parser/metadecoders/decoder.go
index 5bf96b2d88a..fe0581734ee 100644
--- 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
diff --git a/parser/pageparser/pageparser.go b/parser/pageparser/pageparser.go
index 3338e063e1d..bfbedcf2663 100644
--- 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)
 }
diff --git a/publisher/publisher.go b/publisher/publisher.go
index fc1fc0799fd..63eb1011f0d 100644
--- 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.
diff --git a/releaser/releaser.go b/releaser/releaser.go
index cdfba3c7a86..6bcfe3c8098 100644
--- 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
 }
diff --git a/resources/image.go b/resources/image.go
index 253caf735f3..f431784b4a7 100644
--- 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)
diff --git a/resources/images/color.go b/resources/images/color.go
index 0a0db9fe0c5..057a9fb7137 100644
--- 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)
diff --git a/resources/images/config.go b/resources/images/config.go
index d553bda0fc9..62b5c72d8cf 100644
--- 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 == "" {
diff --git a/resources/images/image.go b/resources/images/image.go
index e8cca776988..b12f03b4e33 100644
--- 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...)
diff --git a/resources/page/page_generate/generate_page_wrappers.go b/resources/page/page_generate/generate_page_wrappers.go
index 9d790cc014e..f4b40f71763 100644
--- 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
diff --git a/resources/page/page_matcher.go b/resources/page/page_matcher.go
index 4626186c5cf..c302ff21a9a 100644
--- 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)
 		}
 	}
 
diff --git a/resources/page/pages_related.go b/resources/page/pages_related.go
index 83910e0d4d0..35bb2965a3d 100644
--- 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()
diff --git a/resources/page/permalinks.go b/resources/page/permalinks.go
index aa20da4ed68..cd9c1dc1532 100644
--- 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"
 )
diff --git a/resources/resource.go b/resources/resource.go
index 01f20f09d2f..ead483d65a1 100644
--- 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()
diff --git a/resources/resource_factories/create/remote.go b/resources/resource_factories/create/remote.go
index 32dfafe5cab..51199dc935b 100644
--- 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
diff --git a/resources/resource_metadata.go b/resources/resource_metadata.go
index 6c979b4bfb2..8954a510929 100644
--- 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)
diff --git a/resources/resource_transformers/babel/babel.go b/resources/resource_transformers/babel/babel.go
index a4744961e08..9a9110f62bd 100644
--- 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)
diff --git a/resources/resource_transformers/integrity/integrity.go b/resources/resource_transformers/integrity/integrity.go
index bbd0b6675cd..e1575468581 100644
--- 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)
 	}
 }
 
diff --git a/resources/resource_transformers/js/build.go b/resources/resource_transformers/js/build.go
index 3c6f24fc072..d2fbf5065a3 100644
--- 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)
diff --git a/resources/resource_transformers/js/options.go b/resources/resource_transformers/js/options.go
index 675e40d43ae..a0d1b506fdd 100644
--- 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{
diff --git a/resources/resource_transformers/postcss/postcss.go b/resources/resource_transformers/postcss/postcss.go
index 61209fb23bc..733c958cfa9 100644
--- 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
 }
diff --git a/resources/resource_transformers/templates/execute_as_template.go b/resources/resource_transformers/templates/execute_as_template.go
index 5c2033ff9a3..5fe4230f133 100644
--- 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
diff --git a/resources/resource_transformers/tocss/dartsass/transform.go b/resources/resource_transformers/tocss/dartsass/transform.go
index 79c32fcfdb6..082e30710a1 100644
--- 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
 	}
diff --git a/resources/resource_transformers/tocss/scss/tocss.go b/resources/resource_transformers/tocss/scss/tocss.go
index 802798e59c7..f9f4786f572 100644
--- 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
diff --git a/resources/transform.go b/resources/transform.go
index e269b7b10d6..a470c94daf4 100644
--- 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
diff --git a/source/fileInfo.go b/source/fileInfo.go
index 13c4495bf26..f882eb898b0 100644
--- 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)
diff --git a/source/filesystem.go b/source/filesystem.go
index 4d509c56609..79d027c5c48 100644
--- 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
diff --git a/tpl/cast/cast.go b/tpl/cast/cast.go
index 000e315f1e4..535697f9e38 100644
--- a/tpl/cast/cast.go
+++ b/tpl/cast/cast.go
@@ -29,18 +29,18 @@ func New() *Namespace {
 type Namespace struct {
 }
 
-// ToInt converts the given value to an int.
+// ToInt converts v to an int.
 func (ns *Namespace) ToInt(v any) (int, error) {
 	v = convertTemplateToString(v)
 	return _cast.ToIntE(v)
 }
 
-// ToString converts the given value to a string.
+// ToString converts v to a string.
 func (ns *Namespace) ToString(v any) (string, error) {
 	return _cast.ToStringE(v)
 }
 
-// ToFloat converts the given value to a float.
+// ToFloat converts v to a float.
 func (ns *Namespace) ToFloat(v any) (float64, error) {
 	v = convertTemplateToString(v)
 	return _cast.ToFloat64E(v)
diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go
index 24f43e62c39..516e2c272fb 100644
--- 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)
diff --git a/tpl/collections/merge.go b/tpl/collections/merge.go
index abfc2da9cc2..4d408302b18 100644
--- 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
diff --git a/tpl/collections/reflect_helpers.go b/tpl/collections/reflect_helpers.go
index 4aa137e880f..4178850aa35 100644
--- 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)
 	}
 }
 
diff --git a/tpl/collections/symdiff.go b/tpl/collections/symdiff.go
index 1c411bc35f0..8ecee3c4a97 100644
--- 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)
 				}
diff --git a/tpl/data/data.go b/tpl/data/data.go
index 5e03d52c37f..926f6773d4b 100644
--- 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) {
diff --git a/tpl/data/resources.go b/tpl/data/resources.go
index dca25efb236..0470faf51ae 100644
--- 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)
diff --git a/tpl/fmt/fmt.go b/tpl/fmt/fmt.go
index 3882d470455..7790b495562 100644
--- a/tpl/fmt/fmt.go
+++ b/tpl/fmt/fmt.go
@@ -47,39 +47,39 @@ type Namespace struct {
 	distinctLogger loggers.IgnorableLogger
 }
 
-// Print returns string representation of the passed arguments.
-func (ns *Namespace) Print(a ...any) string {
-	return _fmt.Sprint(a...)
+// Print returns a string representation args.
+func (ns *Namespace) Print(args ...any) string {
+	return _fmt.Sprint(args...)
 }
 
-// Printf returns a formatted string representation of the passed arguments.
-func (ns *Namespace) Printf(format string, a ...any) string {
-	return _fmt.Sprintf(format, a...)
+// Printf returns a formatted string representation of args.
+func (ns *Namespace) Printf(format string, args ...any) string {
+	return _fmt.Sprintf(format, args...)
 }
 
-// Println returns string representation of the passed arguments ending with a newline.
-func (ns *Namespace) Println(a ...any) string {
-	return _fmt.Sprintln(a...)
+// Println returns string representation of args  ending with a newline.
+func (ns *Namespace) Println(args ...any) string {
+	return _fmt.Sprintln(args...)
 }
 
-// Errorf formats according to a format specifier and logs an ERROR.
+// Errorf formats args according to a format specifier and logs an ERROR.
 // It returns an empty string.
-func (ns *Namespace) Errorf(format string, a ...any) string {
-	ns.distinctLogger.Errorf(format, a...)
+func (ns *Namespace) Errorf(format string, args ...any) string {
+	ns.distinctLogger.Errorf(format, args...)
 	return ""
 }
 
-// Erroridf formats according to a format specifier and logs an ERROR and
+// Erroridf formats args according to a format specifier and logs an ERROR and
 // an information text that the error with the given ID can be suppressed in config.
 // It returns an empty string.
-func (ns *Namespace) Erroridf(id, format string, a ...any) string {
-	ns.distinctLogger.Errorsf(id, format, a...)
+func (ns *Namespace) Erroridf(id, format string, args ...any) string {
+	ns.distinctLogger.Errorsf(id, format, args...)
 	return ""
 }
 
-// Warnf formats according to a format specifier and logs a WARNING.
+// Warnf formats args according to a format specifier and logs a WARNING.
 // It returns an empty string.
-func (ns *Namespace) Warnf(format string, a ...any) string {
-	ns.distinctLogger.Warnf(format, a...)
+func (ns *Namespace) Warnf(format string, args ...any) string {
+	ns.distinctLogger.Warnf(format, args...)
 	return ""
 }
diff --git a/tpl/images/images.go b/tpl/images/images.go
index 20af019cf61..1abee1b0c40 100644
--- 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"
 
diff --git a/tpl/internal/resourcehelpers/helpers.go b/tpl/internal/resourcehelpers/helpers.go
index d1c9bbb75ad..2d50c59a4cc 100644
--- 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
diff --git a/tpl/lang/lang.go b/tpl/lang/lang.go
index ee349bd1d70..17d37faa495 100644
--- 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]
 	}
diff --git a/tpl/math/math.go b/tpl/math/math.go
index fce44dc4b1b..257e803e490 100644
--- a/tpl/math/math.go
+++ b/tpl/math/math.go
@@ -32,14 +32,14 @@ func New() *Namespace {
 // Namespace provides template functions for the "math" namespace.
 type Namespace struct{}
 
-// Add adds two numbers.
-func (ns *Namespace) Add(a, b any) (any, error) {
-	return _math.DoArithmetic(a, b, '+')
+// Add adds the two addends n1 and n2.
+func (ns *Namespace) Add(n1, n2 any) (any, error) {
+	return _math.DoArithmetic(n1, n2, '+')
 }
 
-// Ceil returns the least integer value greater than or equal to x.
-func (ns *Namespace) Ceil(x any) (float64, error) {
-	xf, err := cast.ToFloat64E(x)
+// Ceil returns the least integer value greater than or equal to n.
+func (ns *Namespace) Ceil(n any) (float64, error) {
+	xf, err := cast.ToFloat64E(n)
 	if err != nil {
 		return 0, errors.New("Ceil operator can't be used with non-float value")
 	}
@@ -47,14 +47,14 @@ func (ns *Namespace) Ceil(x any) (float64, error) {
 	return math.Ceil(xf), nil
 }
 
-// Div divides two numbers.
-func (ns *Namespace) Div(a, b any) (any, error) {
-	return _math.DoArithmetic(a, b, '/')
+// Div divides n1 by n2.
+func (ns *Namespace) Div(n1, n2 any) (any, error) {
+	return _math.DoArithmetic(n1, n2, '/')
 }
 
-// Floor returns the greatest integer value less than or equal to x.
-func (ns *Namespace) Floor(x any) (float64, error) {
-	xf, err := cast.ToFloat64E(x)
+// Floor returns the greatest integer value less than or equal to n.
+func (ns *Namespace) Floor(n any) (float64, error) {
+	xf, err := cast.ToFloat64E(n)
 	if err != nil {
 		return 0, errors.New("Floor operator can't be used with non-float value")
 	}
@@ -62,9 +62,9 @@ func (ns *Namespace) Floor(x any) (float64, error) {
 	return math.Floor(xf), nil
 }
 
-// Log returns the natural logarithm of a number.
-func (ns *Namespace) Log(a any) (float64, error) {
-	af, err := cast.ToFloat64E(a)
+// Log returns the natural logarithm of the number n.
+func (ns *Namespace) Log(n any) (float64, error) {
+	af, err := cast.ToFloat64E(n)
 	if err != nil {
 		return 0, errors.New("Log operator can't be used with non integer or float value")
 	}
@@ -72,10 +72,10 @@ func (ns *Namespace) Log(a any) (float64, error) {
 	return math.Log(af), nil
 }
 
-// Max returns the greater of two numbers.
-func (ns *Namespace) Max(a, b any) (float64, error) {
-	af, erra := cast.ToFloat64E(a)
-	bf, errb := cast.ToFloat64E(b)
+// Max returns the greater of the two numbers n1 or n2.
+func (ns *Namespace) Max(n1, n2 any) (float64, error) {
+	af, erra := cast.ToFloat64E(n1)
+	bf, errb := cast.ToFloat64E(n2)
 
 	if erra != nil || errb != nil {
 		return 0, errors.New("Max operator can't be used with non-float value")
@@ -84,10 +84,10 @@ func (ns *Namespace) Max(a, b any) (float64, error) {
 	return math.Max(af, bf), nil
 }
 
-// Min returns the smaller of two numbers.
-func (ns *Namespace) Min(a, b any) (float64, error) {
-	af, erra := cast.ToFloat64E(a)
-	bf, errb := cast.ToFloat64E(b)
+// Min returns the smaller of two numbers n1 or n2.
+func (ns *Namespace) Min(n1, n2 any) (float64, error) {
+	af, erra := cast.ToFloat64E(n1)
+	bf, errb := cast.ToFloat64E(n2)
 
 	if erra != nil || errb != nil {
 		return 0, errors.New("Min operator can't be used with non-float value")
@@ -96,10 +96,10 @@ func (ns *Namespace) Min(a, b any) (float64, error) {
 	return math.Min(af, bf), nil
 }
 
-// Mod returns a % b.
-func (ns *Namespace) Mod(a, b any) (int64, error) {
-	ai, erra := cast.ToInt64E(a)
-	bi, errb := cast.ToInt64E(b)
+// Mod returns n1 % n2.
+func (ns *Namespace) Mod(n1, n2 any) (int64, error) {
+	ai, erra := cast.ToInt64E(n1)
+	bi, errb := cast.ToInt64E(n2)
 
 	if erra != nil || errb != nil {
 		return 0, errors.New("modulo operator can't be used with non integer value")
@@ -112,9 +112,9 @@ func (ns *Namespace) Mod(a, b any) (int64, error) {
 	return ai % bi, nil
 }
 
-// ModBool returns the boolean of a % b.  If a % b == 0, return true.
-func (ns *Namespace) ModBool(a, b any) (bool, error) {
-	res, err := ns.Mod(a, b)
+// ModBool returns the boolean of n1 % n2.  If n1 % n2 == 0, return true.
+func (ns *Namespace) ModBool(n1, n2 any) (bool, error) {
+	res, err := ns.Mod(n1, n2)
 	if err != nil {
 		return false, err
 	}
@@ -122,15 +122,15 @@ func (ns *Namespace) ModBool(a, b any) (bool, error) {
 	return res == int64(0), nil
 }
 
-// Mul multiplies two numbers.
-func (ns *Namespace) Mul(a, b any) (any, error) {
-	return _math.DoArithmetic(a, b, '*')
+// Mul multiplies the two numbers n1 and n2.
+func (ns *Namespace) Mul(n1, n2 any) (any, error) {
+	return _math.DoArithmetic(n1, n2, '*')
 }
 
-// Pow returns a raised to the power of b.
-func (ns *Namespace) Pow(a, b any) (float64, error) {
-	af, erra := cast.ToFloat64E(a)
-	bf, errb := cast.ToFloat64E(b)
+// Pow returns n1 raised to the power of n2.
+func (ns *Namespace) Pow(n1, n2 any) (float64, error) {
+	af, erra := cast.ToFloat64E(n1)
+	bf, errb := cast.ToFloat64E(n2)
 
 	if erra != nil || errb != nil {
 		return 0, errors.New("Pow operator can't be used with non-float value")
@@ -139,9 +139,9 @@ func (ns *Namespace) Pow(a, b any) (float64, error) {
 	return math.Pow(af, bf), nil
 }
 
-// Round returns the nearest integer, rounding half away from zero.
-func (ns *Namespace) Round(x any) (float64, error) {
-	xf, err := cast.ToFloat64E(x)
+// Round returns the integer nearest to n, rounding half away from zero.
+func (ns *Namespace) Round(n any) (float64, error) {
+	xf, err := cast.ToFloat64E(n)
 	if err != nil {
 		return 0, errors.New("Round operator can't be used with non-float value")
 	}
@@ -149,9 +149,9 @@ func (ns *Namespace) Round(x any) (float64, error) {
 	return _round(xf), nil
 }
 
-// Sqrt returns the square root of a number.
-func (ns *Namespace) Sqrt(a any) (float64, error) {
-	af, err := cast.ToFloat64E(a)
+// Sqrt returns the square root of the number n.
+func (ns *Namespace) Sqrt(n any) (float64, error) {
+	af, err := cast.ToFloat64E(n)
 	if err != nil {
 		return 0, errors.New("Sqrt operator can't be used with non integer or float value")
 	}
@@ -159,9 +159,9 @@ func (ns *Namespace) Sqrt(a any) (float64, error) {
 	return math.Sqrt(af), nil
 }
 
-// Sub subtracts two numbers.
-func (ns *Namespace) Sub(a, b any) (any, error) {
-	return _math.DoArithmetic(a, b, '-')
+// Sub subtracts n2 from n1.
+func (ns *Namespace) Sub(n1, n2 any) (any, error) {
+	return _math.DoArithmetic(n1, n2, '-')
 }
 
 var counter uint64
diff --git a/tpl/openapi/openapi3/openapi3.go b/tpl/openapi/openapi3/openapi3.go
index 27667388960..1eea04b2562 100644
--- 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()
diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go
index d3a98002fc5..2adec358ce8 100644
--- 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)
 			}
 		}
 	}
diff --git a/tpl/safe/safe.go b/tpl/safe/safe.go
index 6c836b25cbd..d1a2e8d4ef0 100644
--- a/tpl/safe/safe.go
+++ b/tpl/safe/safe.go
@@ -30,44 +30,44 @@ func New() *Namespace {
 // Namespace provides template functions for the "safe" namespace.
 type Namespace struct{}
 
-// CSS returns a given string as html/template CSS content.
-func (ns *Namespace) CSS(a any) (template.CSS, error) {
-	s, err := cast.ToStringE(a)
-	return template.CSS(s), err
+// CSS returns the string s as html/template CSS content.
+func (ns *Namespace) CSS(s any) (template.CSS, error) {
+	ss, err := cast.ToStringE(s)
+	return template.CSS(ss), err
 }
 
-// HTML returns a given string as html/template HTML content.
-func (ns *Namespace) HTML(a any) (template.HTML, error) {
-	s, err := cast.ToStringE(a)
-	return template.HTML(s), err
+// HTML returns the string s as html/template HTML content.
+func (ns *Namespace) HTML(s any) (template.HTML, error) {
+	ss, err := cast.ToStringE(s)
+	return template.HTML(ss), err
 }
 
-// HTMLAttr returns a given string as html/template HTMLAttr content.
-func (ns *Namespace) HTMLAttr(a any) (template.HTMLAttr, error) {
-	s, err := cast.ToStringE(a)
-	return template.HTMLAttr(s), err
+// HTMLAttr returns the string s as html/template HTMLAttr content.
+func (ns *Namespace) HTMLAttr(s any) (template.HTMLAttr, error) {
+	ss, err := cast.ToStringE(s)
+	return template.HTMLAttr(ss), err
 }
 
 // JS returns the given string as a html/template JS content.
-func (ns *Namespace) JS(a any) (template.JS, error) {
-	s, err := cast.ToStringE(a)
-	return template.JS(s), err
+func (ns *Namespace) JS(s any) (template.JS, error) {
+	ss, err := cast.ToStringE(s)
+	return template.JS(ss), err
 }
 
 // JSStr returns the given string as a html/template JSStr content.
-func (ns *Namespace) JSStr(a any) (template.JSStr, error) {
-	s, err := cast.ToStringE(a)
-	return template.JSStr(s), err
+func (ns *Namespace) JSStr(s any) (template.JSStr, error) {
+	ss, err := cast.ToStringE(s)
+	return template.JSStr(ss), err
 }
 
-// URL returns a given string as html/template URL content.
-func (ns *Namespace) URL(a any) (template.URL, error) {
-	s, err := cast.ToStringE(a)
-	return template.URL(s), err
+// URL returns the string s as html/template URL content.
+func (ns *Namespace) URL(s any) (template.URL, error) {
+	ss, err := cast.ToStringE(s)
+	return template.URL(ss), err
 }
 
-// SanitizeURL returns a given string as html/template URL content.
-func (ns *Namespace) SanitizeURL(a any) (string, error) {
-	s, err := cast.ToStringE(a)
-	return helpers.SanitizeURL(s), err
+// SanitizeURL returns the string s as html/template URL content.
+func (ns *Namespace) SanitizeURL(s any) (string, error) {
+	ss, err := cast.ToStringE(s)
+	return helpers.SanitizeURL(ss), err
 }
diff --git a/tpl/strings/strings.go b/tpl/strings/strings.go
index af7c34a5984..482a8a837e9 100644
--- 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
 }
@@ -372,7 +372,7 @@ func (ns *Namespace) Title(s any) (string, error) {
 	return ns.titleFunc(ss), nil
 }
 
-// FirstUpper returns a string with the first character as upper case.
+// FirstUpper converts s making  the first character upper case.
 func (ns *Namespace) FirstUpper(s any) (string, error) {
 	ss, err := cast.ToStringE(s)
 	if err != nil {
@@ -404,8 +404,8 @@ func (ns *Namespace) ToUpper(s any) (string, error) {
 	return strings.ToUpper(ss), nil
 }
 
-// Trim returns a string with all leading and trailing characters defined
-// contained in cutset removed.
+// Trim returns converts the strings s removing all leading and trailing characters defined
+// contained.
 func (ns *Namespace) Trim(s, cutset any) (string, error) {
 	ss, err := cast.ToStringE(s)
 	if err != nil {
@@ -484,7 +484,7 @@ func (ns *Namespace) TrimSuffix(suffix, s any) (string, error) {
 	return strings.TrimSuffix(ss, sx), nil
 }
 
-// Repeat returns a new string consisting of count copies of the string s.
+// Repeat returns a new string consisting of n copies of the string s.
 func (ns *Namespace) Repeat(n, s any) (string, error) {
 	ss, err := cast.ToStringE(s)
 	if err != nil {
diff --git a/tpl/time/time.go b/tpl/time/time.go
index f82d63c4402..623b7206aa6 100644
--- a/tpl/time/time.go
+++ b/tpl/time/time.go
@@ -57,9 +57,8 @@ func (ns *Namespace) AsTime(v any, args ...any) (any, error) {
 
 }
 
-// Format converts the textual representation of the datetime string into
-// the other form or returns it of the time.Time value. These are formatted
-// with the layout string
+// Format converts the textual representation of the datetime string in v into
+// time.Time if needed and formats it with the given layout.
 func (ns *Namespace) Format(layout string, v any) (string, error) {
 	t, err := htime.ToTimeInDefaultLocationE(v, ns.location)
 	if err != nil {
@@ -74,19 +73,19 @@ func (ns *Namespace) Now() _time.Time {
 	return _time.Now()
 }
 
-// ParseDuration parses a duration string.
+// ParseDuration parses the duration string s.
 // A duration string is a possibly signed sequence of
 // decimal numbers, each with optional fraction and a unit suffix,
 // such as "300ms", "-1.5h" or "2h45m".
 // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
 // See https://golang.org/pkg/time/#ParseDuration
-func (ns *Namespace) ParseDuration(in any) (_time.Duration, error) {
-	s, err := cast.ToStringE(in)
+func (ns *Namespace) ParseDuration(s any) (_time.Duration, error) {
+	ss, err := cast.ToStringE(s)
 	if err != nil {
 		return 0, err
 	}
 
-	return _time.ParseDuration(s)
+	return _time.ParseDuration(ss)
 }
 
 var durationUnits = map[string]_time.Duration{
diff --git a/tpl/tplimpl/embedded/templates/server/error.html b/tpl/tplimpl/embedded/templates/server/error.html
new file mode 100644
index 00000000000..e5c6d0cbf3f
--- /dev/null
+++ 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>
diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
index 4cee19a2015..c092ff63816 100644
--- 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
 		}
 
diff --git a/tpl/tplimpl/template_ast_transformers.go b/tpl/tplimpl/template_ast_transformers.go
index 33461dc7d23..dee1fd6c2ec 100644
--- 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)
 		}
 	}
 }
diff --git a/tpl/tplimpl/template_errors.go b/tpl/tplimpl/template_errors.go
index 06d89553693..d553a12e94a 100644
--- 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
 }
diff --git a/tpl/transform/remarshal.go b/tpl/transform/remarshal.go
index b5d6821442a..0ad54bb961a 100644
--- 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"
diff --git a/tpl/transform/transform.go b/tpl/transform/transform.go
index 2f62557dea6..7c091437847 100644
--- a/tpl/transform/transform.go
+++ b/tpl/transform/transform.go
@@ -90,9 +90,9 @@ func (ns *Namespace) HighlightCodeBlock(ctx hooks.CodeblockContext, opts ...any)
 	return hl.HighlightCodeBlock(ctx, optsv)
 }
 
-// CanHighlight returns whether the given language is supported by the Chroma highlighter.
-func (ns *Namespace) CanHighlight(lang string) bool {
-	return lexers.Get(lang) != nil
+// CanHighlight returns whether the given code language is supported by the Chroma highlighter.
+func (ns *Namespace) CanHighlight(language string) bool {
+	return lexers.Get(language) != nil
 }
 
 // HTMLEscape returns a copy of s with reserved HTML characters escaped.
@@ -105,7 +105,7 @@ func (ns *Namespace) HTMLEscape(s any) (string, error) {
 	return html.EscapeString(ss), nil
 }
 
-// HTMLUnescape returns a copy of with HTML escape requences converted to plain
+// HTMLUnescape returns a copy of s with HTML escape requences converted to plain
 // text.
 func (ns *Namespace) HTMLUnescape(s any) (string, error) {
 	ss, err := cast.ToStringE(s)
@@ -116,7 +116,7 @@ func (ns *Namespace) HTMLUnescape(s any) (string, error) {
 	return html.UnescapeString(ss), nil
 }
 
-// Markdownify renders a given input from Markdown to HTML.
+// Markdownify renders s from Markdown to HTML.
 func (ns *Namespace) Markdownify(s any) (template.HTML, error) {
 
 	home := ns.deps.Site.Home()
@@ -144,6 +144,7 @@ func (ns *Namespace) Plainify(s any) (string, error) {
 	return helpers.StripHTML(ss), nil
 }
 
+// For internal use.
 func (ns *Namespace) Reset() {
 	ns.cache.Clear()
 }
diff --git a/tpl/transform/unmarshal.go b/tpl/transform/unmarshal.go
index 62682557c0a..340235fa444 100644
--- 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)
 		}
 	}
 
diff --git a/tpl/urls/urls.go b/tpl/urls/urls.go
index e071062619a..bfbd7304ffb 100644
--- 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"
 )
 
@@ -40,14 +39,14 @@ type Namespace struct {
 	multihost bool
 }
 
-// AbsURL takes a given string and converts it to an absolute URL.
-func (ns *Namespace) AbsURL(a any) (template.HTML, error) {
-	s, err := cast.ToStringE(a)
+// AbsURL takes the string s and converts it to an absolute URL.
+func (ns *Namespace) AbsURL(s any) (template.HTML, error) {
+	ss, err := cast.ToStringE(s)
 	if err != nil {
 		return "", nil
 	}
 
-	return template.HTML(ns.deps.PathSpec.AbsURL(s, false)), nil
+	return template.HTML(ns.deps.PathSpec.AbsURL(ss, false)), nil
 }
 
 // Parse parses rawurl into a URL structure. The rawurl may be relative or
@@ -55,44 +54,45 @@ func (ns *Namespace) AbsURL(a 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)
 }
 
-// RelURL takes a given string and prepends the relative path according to a
+// RelURL takes the string s and prepends the relative path according to a
 // page's position in the project directory structure.
-func (ns *Namespace) RelURL(a any) (template.HTML, error) {
-	s, err := cast.ToStringE(a)
+func (ns *Namespace) RelURL(s any) (template.HTML, error) {
+	ss, err := cast.ToStringE(s)
 	if err != nil {
 		return "", nil
 	}
 
-	return template.HTML(ns.deps.PathSpec.RelURL(s, false)), nil
+	return template.HTML(ns.deps.PathSpec.RelURL(ss, false)), nil
 }
 
-// URLize returns the given argument formatted as URL.
-func (ns *Namespace) URLize(a any) (string, error) {
-	s, err := cast.ToStringE(a)
+// URLize returns the the strings s formatted as an URL.
+func (ns *Namespace) URLize(s any) (string, error) {
+	ss, err := cast.ToStringE(s)
 	if err != nil {
 		return "", nil
 	}
-	return ns.deps.PathSpec.URLize(s), nil
+	return ns.deps.PathSpec.URLize(ss), nil
 }
 
-// Anchorize creates sanitized anchor names that are compatible with Blackfriday.
-func (ns *Namespace) Anchorize(a any) (string, error) {
-	s, err := cast.ToStringE(a)
+// Anchorize creates sanitized anchor name version of the string s that is compatible
+// with how your configured markdown renderer does it.
+func (ns *Namespace) Anchorize(s any) (string, error) {
+	ss, err := cast.ToStringE(s)
 	if err != nil {
 		return "", nil
 	}
-	return ns.deps.ContentSpec.SanitizeAnchorName(s), nil
+	return ns.deps.ContentSpec.SanitizeAnchorName(ss), nil
 }
 
-// Ref returns the absolute URL path to a given content item.
-func (ns *Namespace) Ref(in any, args any) (template.HTML, error) {
-	p, ok := in.(urls.RefLinker)
+// Ref returns the absolute URL path to a given content item from Page p.
+func (ns *Namespace) Ref(p any, args any) (template.HTML, error) {
+	pp, ok := p.(urls.RefLinker)
 	if !ok {
 		return "", errors.New("invalid Page received in Ref")
 	}
@@ -100,13 +100,13 @@ func (ns *Namespace) Ref(in any, args any) (template.HTML, error) {
 	if err != nil {
 		return "", err
 	}
-	s, err := p.Ref(argsm)
+	s, err := pp.Ref(argsm)
 	return template.HTML(s), err
 }
 
-// RelRef returns the relative URL path to a given content item.
-func (ns *Namespace) RelRef(in any, args any) (template.HTML, error) {
-	p, ok := in.(urls.RefLinker)
+// RelRef returns the relative URL path to a given content item from Page p.
+func (ns *Namespace) RelRef(p any, args any) (template.HTML, error) {
+	pp, ok := p.(urls.RefLinker)
 	if !ok {
 		return "", errors.New("invalid Page received in RelRef")
 	}
@@ -115,7 +115,7 @@ func (ns *Namespace) RelRef(in any, args any) (template.HTML, error) {
 		return "", err
 	}
 
-	s, err := p.RelRef(argsm)
+	s, err := pp.RelRef(argsm)
 	return template.HTML(s), err
 }
 
@@ -163,25 +163,25 @@ func (ns *Namespace) refArgsToMap(args any) (map[string]any, error) {
 	}, nil
 }
 
-// RelLangURL takes a given string and prepends the relative path according to a
+// RelLangURL takes the string s and prepends the relative path according to a
 // page's position in the project directory structure and the current language.
-func (ns *Namespace) RelLangURL(a any) (template.HTML, error) {
-	s, err := cast.ToStringE(a)
+func (ns *Namespace) RelLangURL(s any) (template.HTML, error) {
+	ss, err := cast.ToStringE(s)
 	if err != nil {
 		return "", err
 	}
 
-	return template.HTML(ns.deps.PathSpec.RelURL(s, !ns.multihost)), nil
+	return template.HTML(ns.deps.PathSpec.RelURL(ss, !ns.multihost)), nil
 }
 
-// AbsLangURL takes a given string and converts it to an absolute URL according
+// AbsLangURL the string s and converts it to an absolute URL according
 // to a page's position in the project directory structure and the current
 // language.
-func (ns *Namespace) AbsLangURL(a any) (template.HTML, error) {
-	s, err := cast.ToStringE(a)
+func (ns *Namespace) AbsLangURL(s any) (template.HTML, error) {
+	ss, err := cast.ToStringE(s)
 	if err != nil {
 		return "", err
 	}
 
-	return template.HTML(ns.deps.PathSpec.AbsURL(s, !ns.multihost)), nil
+	return template.HTML(ns.deps.PathSpec.AbsURL(ss, !ns.multihost)), nil
 }
diff --git a/transform/chain.go b/transform/chain.go
index 74217dc7226..2d70b93562c 100644
--- 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)
+
 		}
 	}
 


Test patch:
diff --git a/common/herrors/error_locator_test.go b/common/herrors/error_locator_test.go
index 0cd5fb2d74a..10b016fa82b 100644
--- 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)
 }
diff --git a/common/herrors/file_error_test.go b/common/herrors/file_error_test.go
index 675be94e877..e6595aa28c3 100644
--- 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))
 	}
 }
diff --git a/hugofs/walk_test.go b/hugofs/walk_test.go
index 2d9ec784d57..2e162fa7213 100644
--- 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"
diff --git a/hugolib/configdir_test.go b/hugolib/configdir_test.go
index 99801031848..7ac3f969dac 100644
--- 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"))
 }
diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go
index 8b23e7ac734..8f983075da1 100644
--- 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
diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
index 58e75189270..c3fb9fffc1c 100644
--- 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) {
diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go
index c2c5abe8723..d1a84442390 100644
--- 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) {
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
index 6c1aa6fd09d..4a72836af62 100644
--- 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")