Repository: helm/helm
Pull request #13460 (2024-11-18 17:57:03)

fix: replace "github.com/pkg/errors" with stdlib "errors" package
**What this PR does / why we need it**:

Replaces the archived "github.com/pkg/errors" package with the Go standard library equivalents, and updates error handling to use more recent standard library features.

**Special notes for your reviewer**:

Updates are generally:

- Replace `errors.Wrap(err, "msg")` and `errors.Wrapf(err, "msg")` with `fmt.Errorf("msg: %w", err)`
- Replace `errors.Errorf("msg", args)` with `fmt.Errorf("msg", args)`
- Replace uses of `[]string` and `strings.Join` for error messages with `[]error` and `errors.Join`
  - Used a simple wrapper type ([example](https://github.com/helm/helm/pull/13460/files#diff-4d76035a2539b2daa4c488f8494ef0a48903cc5e83558d3e2fffcafa553d00b8R187)) if the error messages were being joined with any string other than `"\n"` so they will render the same as before
- Replace `os.IsNotExist(err)` with `errors.Is(err, fs.ErrNotExist)`

**If applicable**:
- [ ] this PR contains user facing changes (the `docs needed` label should be applied if so)
- [ ] this PR contains unit tests
- [x] this PR has been tested for backwards compatibility



Patch:
diff --git a/.golangci.yml b/.golangci.yml
index a34e5f538..d8401bdd6 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -20,6 +20,8 @@ linters:
           deny:
             - pkg: github.com/hashicorp/go-multierror
               desc: "use errors instead"
+            - pkg: github.com/pkg/errors
+              desc: "use errors instead"
     dupl:
       threshold: 400
   exclusions:
diff --git a/go.mod b/go.mod
index 0a99cf330..7474584bc 100644
--- a/go.mod
+++ b/go.mod
@@ -26,7 +26,6 @@ require (
 	github.com/moby/term v0.5.2
 	github.com/opencontainers/image-spec v1.1.1
 	github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
-	github.com/pkg/errors v0.9.1
 	github.com/rubenv/sql-migrate v1.8.0
 	github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
 	github.com/spf13/cobra v1.9.1
@@ -120,6 +119,7 @@ require (
 	github.com/onsi/gomega v1.36.2 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
+	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/prometheus/client_golang v1.20.5 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go
index 42c9de3b7..13dcd2ce9 100644
--- a/internal/resolver/resolver.go
+++ b/internal/resolver/resolver.go
@@ -18,14 +18,15 @@ package resolver
 import (
 	"bytes"
 	"encoding/json"
+	"errors"
 	"fmt"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"strings"
 	"time"
 
 	"github.com/Masterminds/semver/v3"
-	"github.com/pkg/errors"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
 	"helm.sh/helm/v4/pkg/chart/v2/loader"
@@ -60,7 +61,7 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
 	for i, d := range reqs {
 		constraint, err := semver.NewConstraint(d.Version)
 		if err != nil {
-			return nil, errors.Wrapf(err, "dependency %q has an invalid version/constraint format", d.Name)
+			return nil, fmt.Errorf("dependency %q has an invalid version/constraint format: %w", d.Name, err)
 		}
 
 		if d.Repository == "" {
@@ -124,12 +125,12 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
 		if !registry.IsOCI(d.Repository) {
 			repoIndex, err := repo.LoadIndexFile(filepath.Join(r.cachepath, helmpath.CacheIndexFile(repoName)))
 			if err != nil {
-				return nil, errors.Wrapf(err, "no cached repository for %s found. (try 'helm repo update')", repoName)
+				return nil, fmt.Errorf("no cached repository for %s found. (try 'helm repo update'): %w", repoName, err)
 			}
 
 			vs, ok = repoIndex.Entries[d.Name]
 			if !ok {
-				return nil, errors.Errorf("%s chart not found in repo %s", d.Name, d.Repository)
+				return nil, fmt.Errorf("%s chart not found in repo %s", d.Name, d.Repository)
 			}
 			found = false
 		} else {
@@ -151,7 +152,7 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
 				ref := fmt.Sprintf("%s/%s", strings.TrimPrefix(d.Repository, fmt.Sprintf("%s://", registry.OCIScheme)), d.Name)
 				tags, err := r.registryClient.Tags(ref)
 				if err != nil {
-					return nil, errors.Wrapf(err, "could not retrieve list of tags for repository %s", d.Repository)
+					return nil, fmt.Errorf("could not retrieve list of tags for repository %s: %w", d.Repository, err)
 				}
 
 				vs = make(repo.ChartVersions, len(tags))
@@ -192,7 +193,7 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
 		}
 	}
 	if len(missing) > 0 {
-		return nil, errors.Errorf("can't get a valid version for %d subchart(s): %s. Make sure a matching chart version exists in the repo, or change the version constraint in Chart.yaml", len(missing), strings.Join(missing, ", "))
+		return nil, fmt.Errorf("can't get a valid version for %d subchart(s): %s. Make sure a matching chart version exists in the repo, or change the version constraint in Chart.yaml", len(missing), strings.Join(missing, ", "))
 	}
 
 	digest, err := HashReq(reqs, locked)
@@ -252,8 +253,8 @@ func GetLocalPath(repo, chartpath string) (string, error) {
 		depPath = filepath.Join(chartpath, p)
 	}
 
-	if _, err = os.Stat(depPath); os.IsNotExist(err) {
-		return "", errors.Errorf("directory %s not found", depPath)
+	if _, err = os.Stat(depPath); errors.Is(err, fs.ErrNotExist) {
+		return "", fmt.Errorf("directory %s not found", depPath)
 	} else if err != nil {
 		return "", err
 	}
diff --git a/internal/sympath/walk.go b/internal/sympath/walk.go
index 0cd258d39..f67b9f1b9 100644
--- a/internal/sympath/walk.go
+++ b/internal/sympath/walk.go
@@ -21,12 +21,11 @@ limitations under the License.
 package sympath
 
 import (
+	"fmt"
 	"log/slog"
 	"os"
 	"path/filepath"
 	"sort"
-
-	"github.com/pkg/errors"
 )
 
 // Walk walks the file tree rooted at root, calling walkFn for each file or directory
@@ -69,7 +68,7 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
 	if IsSymlink(info) {
 		resolved, err := filepath.EvalSymlinks(path)
 		if err != nil {
-			return errors.Wrapf(err, "error evaluating symlink %s", path)
+			return fmt.Errorf("error evaluating symlink %s: %w", path, err)
 		}
 		//This log message is to highlight a symlink that is being used within a chart, symlinks can be used for nefarious reasons.
 		slog.Info("found symbolic link in path. Contents of linked file included and used", "path", path, "resolved", resolved)
diff --git a/internal/third_party/dep/fs/fs.go b/internal/third_party/dep/fs/fs.go
index 8202ee1d5..717eff04d 100644
--- a/internal/third_party/dep/fs/fs.go
+++ b/internal/third_party/dep/fs/fs.go
@@ -32,13 +32,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 package fs
 
 import (
+	"errors"
+	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"runtime"
 	"syscall"
-
-	"github.com/pkg/errors"
 )
 
 // fs contains a copy of a few functions from dep tool code to avoid a dependency on golang/dep.
@@ -51,7 +52,7 @@ import (
 func RenameWithFallback(src, dst string) error {
 	_, err := os.Stat(src)
 	if err != nil {
-		return errors.Wrapf(err, "cannot stat %s", src)
+		return fmt.Errorf("cannot stat %s: %w", src, err)
 	}
 
 	err = os.Rename(src, dst)
@@ -69,20 +70,24 @@ func renameByCopy(src, dst string) error {
 	if dir, _ := IsDir(src); dir {
 		cerr = CopyDir(src, dst)
 		if cerr != nil {
-			cerr = errors.Wrap(cerr, "copying directory failed")
+			cerr = fmt.Errorf("copying directory failed: %w", cerr)
 		}
 	} else {
 		cerr = copyFile(src, dst)
 		if cerr != nil {
-			cerr = errors.Wrap(cerr, "copying file failed")
+			cerr = fmt.Errorf("copying file failed: %w", cerr)
 		}
 	}
 
 	if cerr != nil {
-		return errors.Wrapf(cerr, "rename fallback failed: cannot rename %s to %s", src, dst)
+		return fmt.Errorf("rename fallback failed: cannot rename %s to %s: %w", src, dst, cerr)
+	}
+
+	if err := os.RemoveAll(src); err != nil {
+		return fmt.Errorf("cannot delete %s: %w", src, err)
 	}
 
-	return errors.Wrapf(os.RemoveAll(src), "cannot delete %s", src)
+	return nil
 }
 
 var (
@@ -107,7 +112,7 @@ func CopyDir(src, dst string) error {
 	}
 
 	_, err = os.Stat(dst)
-	if err != nil && !os.IsNotExist(err) {
+	if err != nil && !errors.Is(err, fs.ErrNotExist) {
 		return err
 	}
 	if err == nil {
@@ -115,12 +120,12 @@ func CopyDir(src, dst string) error {
 	}
 
 	if err = os.MkdirAll(dst, fi.Mode()); err != nil {
-		return errors.Wrapf(err, "cannot mkdir %s", dst)
+		return fmt.Errorf("cannot mkdir %s: %w", dst, err)
 	}
 
 	entries, err := os.ReadDir(src)
 	if err != nil {
-		return errors.Wrapf(err, "cannot read directory %s", dst)
+		return fmt.Errorf("cannot read directory %s: %w", dst, err)
 	}
 
 	for _, entry := range entries {
@@ -129,13 +134,13 @@ func CopyDir(src, dst string) error {
 
 		if entry.IsDir() {
 			if err = CopyDir(srcPath, dstPath); err != nil {
-				return errors.Wrap(err, "copying directory failed")
+				return fmt.Errorf("copying directory failed: %w", err)
 			}
 		} else {
 			// This will include symlinks, which is what we want when
 			// copying things.
 			if err = copyFile(srcPath, dstPath); err != nil {
-				return errors.Wrap(err, "copying file failed")
+				return fmt.Errorf("copying file failed: %w", err)
 			}
 		}
 	}
@@ -149,7 +154,7 @@ func CopyDir(src, dst string) error {
 // of the source file. The file mode will be copied from the source.
 func copyFile(src, dst string) (err error) {
 	if sym, err := IsSymlink(src); err != nil {
-		return errors.Wrap(err, "symlink check failed")
+		return fmt.Errorf("symlink check failed: %w", err)
 	} else if sym {
 		if err := cloneSymlink(src, dst); err != nil {
 			if runtime.GOOS == "windows" {
@@ -226,7 +231,7 @@ func IsDir(name string) (bool, error) {
 		return false, err
 	}
 	if !fi.IsDir() {
-		return false, errors.Errorf("%q is not a directory", name)
+		return false, fmt.Errorf("%q is not a directory", name)
 	}
 	return true, nil
 }
diff --git a/internal/third_party/dep/fs/rename.go b/internal/third_party/dep/fs/rename.go
index a3e5e56a6..662accffa 100644
--- a/internal/third_party/dep/fs/rename.go
+++ b/internal/third_party/dep/fs/rename.go
@@ -34,10 +34,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 package fs
 
 import (
+	"fmt"
 	"os"
 	"syscall"
-
-	"github.com/pkg/errors"
 )
 
 // renameFallback attempts to determine the appropriate fallback to failed rename
@@ -51,7 +50,7 @@ func renameFallback(err error, src, dst string) error {
 	if !ok {
 		return err
 	} else if terr.Err != syscall.EXDEV {
-		return errors.Wrapf(terr, "link error: cannot rename %s to %s", src, dst)
+		return fmt.Errorf("link error: cannot rename %s to %s: %w", src, dst, err)
 	}
 
 	return renameByCopy(src, dst)
diff --git a/internal/third_party/dep/fs/rename_windows.go b/internal/third_party/dep/fs/rename_windows.go
index a377720a6..3c8e64883 100644
--- a/internal/third_party/dep/fs/rename_windows.go
+++ b/internal/third_party/dep/fs/rename_windows.go
@@ -34,10 +34,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 package fs
 
 import (
+	"errors"
 	"os"
 	"syscall"
-
-	"github.com/pkg/errors"
 )
 
 // renameFallback attempts to determine the appropriate fallback to failed rename
diff --git a/pkg/action/action.go b/pkg/action/action.go
index e91054a28..6905f3f44 100644
--- a/pkg/action/action.go
+++ b/pkg/action/action.go
@@ -18,6 +18,7 @@ package action
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
 	"io"
 	"log/slog"
@@ -27,7 +28,6 @@ import (
 	"strings"
 	"text/template"
 
-	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	"k8s.io/client-go/discovery"
@@ -105,7 +105,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
 
 	if ch.Metadata.KubeVersion != "" {
 		if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) {
-			return hs, b, "", errors.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String())
+			return hs, b, "", fmt.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String())
 		}
 	}
 
@@ -220,7 +220,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
 	if pr != nil {
 		b, err = pr.Run(b)
 		if err != nil {
-			return hs, b, notes, errors.Wrap(err, "error while running post render on files")
+			return hs, b, notes, fmt.Errorf("error while running post render on files: %w", err)
 		}
 	}
 
@@ -241,13 +241,13 @@ func (cfg *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
 	}
 	dc, err := cfg.RESTClientGetter.ToDiscoveryClient()
 	if err != nil {
-		return nil, errors.Wrap(err, "could not get Kubernetes discovery client")
+		return nil, fmt.Errorf("could not get Kubernetes discovery client: %w", err)
 	}
 	// force a discovery cache invalidation to always fetch the latest server version/capabilities.
 	dc.Invalidate()
 	kubeVersion, err := dc.ServerVersion()
 	if err != nil {
-		return nil, errors.Wrap(err, "could not get server version from Kubernetes")
+		return nil, fmt.Errorf("could not get server version from Kubernetes: %w", err)
 	}
 	// Issue #6361:
 	// Client-Go emits an error when an API service is registered but unimplemented.
@@ -260,7 +260,7 @@ func (cfg *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
 			slog.Warn("the kubernetes server has an orphaned API service", slog.Any("error", err))
 			slog.Warn("to fix this, kubectl delete apiservice <service-name>")
 		} else {
-			return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes")
+			return nil, fmt.Errorf("could not get apiVersions from Kubernetes: %w", err)
 		}
 	}
 
@@ -280,7 +280,7 @@ func (cfg *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
 func (cfg *Configuration) KubernetesClientSet() (kubernetes.Interface, error) {
 	conf, err := cfg.RESTClientGetter.ToRESTConfig()
 	if err != nil {
-		return nil, errors.Wrap(err, "unable to generate config for kubernetes client")
+		return nil, fmt.Errorf("unable to generate config for kubernetes client: %w", err)
 	}
 
 	return kubernetes.NewForConfig(conf)
@@ -296,7 +296,7 @@ func (cfg *Configuration) Now() time.Time {
 
 func (cfg *Configuration) releaseContent(name string, version int) (*release.Release, error) {
 	if err := chartutil.ValidateReleaseName(name); err != nil {
-		return nil, errors.Errorf("releaseContent: Release name is invalid: %s", name)
+		return nil, fmt.Errorf("releaseContent: Release name is invalid: %s", name)
 	}
 
 	if version <= 0 {
@@ -310,7 +310,7 @@ func (cfg *Configuration) releaseContent(name string, version int) (*release.Rel
 func GetVersionSet(client discovery.ServerResourcesInterface) (chartutil.VersionSet, error) {
 	groups, resources, err := client.ServerGroupsAndResources()
 	if err != nil && !discovery.IsGroupDiscoveryFailedError(err) {
-		return chartutil.DefaultVersionSet, errors.Wrap(err, "could not get apiVersions from Kubernetes")
+		return chartutil.DefaultVersionSet, fmt.Errorf("could not get apiVersions from Kubernetes: %w", err)
 	}
 
 	// FIXME: The Kubernetes test fixture for cli appears to always return nil
@@ -399,11 +399,11 @@ func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namesp
 			namespace,
 		)
 		if err != nil {
-			return errors.Wrap(err, "unable to instantiate SQL driver")
+			return fmt.Errorf("unable to instantiate SQL driver: %w", err)
 		}
 		store = storage.Init(d)
 	default:
-		return errors.Errorf("unknown driver %q", helmDriver)
+		return fmt.Errorf("unknown driver %q", helmDriver)
 	}
 
 	cfg.RESTClientGetter = getter
diff --git a/pkg/action/history.go b/pkg/action/history.go
index b8e472195..d7af1d6a4 100644
--- a/pkg/action/history.go
+++ b/pkg/action/history.go
@@ -19,7 +19,7 @@ package action
 import (
 	"log/slog"
 
-	"github.com/pkg/errors"
+	"fmt"
 
 	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
 	release "helm.sh/helm/v4/pkg/release/v1"
@@ -52,7 +52,7 @@ func (h *History) Run(name string) ([]*release.Release, error) {
 	}
 
 	if err := chartutil.ValidateReleaseName(name); err != nil {
-		return nil, errors.Errorf("release name is invalid: %s", name)
+		return nil, fmt.Errorf("release name is invalid: %s", name)
 	}
 
 	slog.Debug("getting history for release", "release", name)
diff --git a/pkg/action/hooks.go b/pkg/action/hooks.go
index dbacb123e..8db0d51f8 100644
--- a/pkg/action/hooks.go
+++ b/pkg/action/hooks.go
@@ -25,10 +25,8 @@ import (
 
 	"helm.sh/helm/v4/pkg/kube"
 
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	"github.com/pkg/errors"
 	"gopkg.in/yaml.v3"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	release "helm.sh/helm/v4/pkg/release/v1"
 	helmtime "helm.sh/helm/v4/pkg/time"
@@ -65,7 +63,7 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
 
 		resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest), true)
 		if err != nil {
-			return errors.Wrapf(err, "unable to build kubernetes object for %s hook %s", hook, h.Path)
+			return fmt.Errorf("unable to build kubernetes object for %s hook %s: %w", hook, h.Path, err)
 		}
 
 		// Record the time at which the hook was applied to the cluster
@@ -84,12 +82,12 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
 		if _, err := cfg.KubeClient.Create(resources); err != nil {
 			h.LastRun.CompletedAt = helmtime.Now()
 			h.LastRun.Phase = release.HookPhaseFailed
-			return errors.Wrapf(err, "warning: Hook %s %s failed", hook, h.Path)
+			return fmt.Errorf("warning: Hook %s %s failed: %w", hook, h.Path, err)
 		}
 
 		waiter, err := cfg.KubeClient.GetWaiter(waitStrategy)
 		if err != nil {
-			return errors.Wrapf(err, "unable to get waiter")
+			return fmt.Errorf("unable to get waiter: %w", err)
 		}
 		// Watch hook resources until they have completed
 		err = waiter.WatchUntilReady(resources, timeout)
@@ -159,11 +157,11 @@ func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.Hoo
 	if hookHasDeletePolicy(h, policy) {
 		resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest), false)
 		if err != nil {
-			return errors.Wrapf(err, "unable to build kubernetes object for deleting hook %s", h.Path)
+			return fmt.Errorf("unable to build kubernetes object for deleting hook %s: %w", h.Path, err)
 		}
 		_, errs := cfg.KubeClient.Delete(resources)
 		if len(errs) > 0 {
-			return errors.New(joinErrors(errs))
+			return joinErrors(errs, "; ")
 		}
 
 		waiter, err := cfg.KubeClient.GetWaiter(waitStrategy)
@@ -239,7 +237,7 @@ func (cfg *Configuration) deriveNamespace(h *release.Hook, namespace string) (st
 	}{}
 	err := yaml.Unmarshal([]byte(h.Manifest), &tmp)
 	if err != nil {
-		return "", errors.Wrapf(err, "unable to parse metadata.namespace from kubernetes manifest for output logs hook %s", h.Path)
+		return "", fmt.Errorf("unable to parse metadata.namespace from kubernetes manifest for output logs hook %s: %w", h.Path, err)
 	}
 	if tmp.Metadata.Namespace == "" {
 		return namespace, nil
diff --git a/pkg/action/install.go b/pkg/action/install.go
index 609703602..440f41baa 100644
--- a/pkg/action/install.go
+++ b/pkg/action/install.go
@@ -19,8 +19,10 @@ package action
 import (
 	"bytes"
 	"context"
+	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"log/slog"
 	"net/url"
 	"os"
@@ -32,7 +34,6 @@ import (
 	"time"
 
 	"github.com/Masterminds/sprig/v3"
-	"github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
@@ -165,7 +166,7 @@ func (i *Install) installCRDs(crds []chart.CRD) error {
 		// Read in the resources
 		res, err := i.cfg.KubeClient.Build(bytes.NewBuffer(obj.File.Data), false)
 		if err != nil {
-			return errors.Wrapf(err, "failed to install CRD %s", obj.Name)
+			return fmt.Errorf("failed to install CRD %s: %w", obj.Name, err)
 		}
 
 		// Send them to Kube
@@ -176,14 +177,14 @@ func (i *Install) installCRDs(crds []chart.CRD) error {
 				slog.Debug("CRD is already present. Skipping", "crd", crdName)
 				continue
 			}
-			return errors.Wrapf(err, "failed to install CRD %s", obj.Name)
+			return fmt.Errorf("failed to install CRD %s: %w", obj.Name, err)
 		}
 		totalItems = append(totalItems, res...)
 	}
 	if len(totalItems) > 0 {
 		waiter, err := i.cfg.KubeClient.GetWaiter(i.WaitStrategy)
 		if err != nil {
-			return errors.Wrapf(err, "unable to get waiter")
+			return fmt.Errorf("unable to get waiter: %w", err)
 		}
 		// Give time for the CRD to be recognized.
 		if err := waiter.Wait(totalItems, 60*time.Second); err != nil {
@@ -239,24 +240,24 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
 	if !i.ClientOnly {
 		if err := i.cfg.KubeClient.IsReachable(); err != nil {
 			slog.Error(fmt.Sprintf("cluster reachability check failed: %v", err))
-			return nil, errors.Wrap(err, "cluster reachability check failed")
+			return nil, fmt.Errorf("cluster reachability check failed: %w", err)
 		}
 	}
 
 	// HideSecret must be used with dry run. Otherwise, return an error.
 	if !i.isDryRun() && i.HideSecret {
 		slog.Error("hiding Kubernetes secrets requires a dry-run mode")
-		return nil, errors.New("Hiding Kubernetes secrets requires a dry-run mode")
+		return nil, errors.New("hiding Kubernetes secrets requires a dry-run mode")
 	}
 
 	if err := i.availableName(); err != nil {
 		slog.Error("release name check failed", slog.Any("error", err))
-		return nil, errors.Wrap(err, "release name check failed")
+		return nil, fmt.Errorf("release name check failed: %w", err)
 	}
 
 	if err := chartutil.ProcessDependencies(chrt, vals); err != nil {
 		slog.Error("chart dependencies processing failed", slog.Any("error", err))
-		return nil, errors.Wrap(err, "chart dependencies processing failed")
+		return nil, fmt.Errorf("chart dependencies processing failed: %w", err)
 	}
 
 	var interactWithRemote bool
@@ -342,7 +343,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
 	var toBeAdopted kube.ResourceList
 	resources, err := i.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest), !i.DisableOpenAPIValidation)
 	if err != nil {
-		return nil, errors.Wrap(err, "unable to build kubernetes objects from release manifest")
+		return nil, fmt.Errorf("unable to build kubernetes objects from release manifest: %w", err)
 	}
 
 	// It is safe to use "force" here because these are resources currently rendered by the chart.
@@ -364,7 +365,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
 			toBeAdopted, err = existingResourceConflict(resources, rel.Name, rel.Namespace)
 		}
 		if err != nil {
-			return nil, errors.Wrap(err, "Unable to continue with install")
+			return nil, fmt.Errorf("unable to continue with install: %w", err)
 		}
 	}
 
@@ -525,9 +526,9 @@ func (i *Install) failRelease(rel *release.Release, err error) (*release.Release
 		uninstall.KeepHistory = false
 		uninstall.Timeout = i.Timeout
 		if _, uninstallErr := uninstall.Run(i.ReleaseName); uninstallErr != nil {
-			return rel, errors.Wrapf(uninstallErr, "an error occurred while uninstalling the release. original install error: %s", err)
+			return rel, fmt.Errorf("an error occurred while uninstalling the release. original install error: %w: %w", err, uninstallErr)
 		}
-		return rel, errors.Wrapf(err, "release %s failed, and has been uninstalled due to atomic being set", i.ReleaseName)
+		return rel, fmt.Errorf("release %s failed, and has been uninstalled due to atomic being set: %w", i.ReleaseName, err)
 	}
 	i.recordRelease(rel) // Ignore the error, since we have another error to deal with.
 	return rel, err
@@ -545,7 +546,7 @@ func (i *Install) availableName() error {
 	start := i.ReleaseName
 
 	if err := chartutil.ValidateReleaseName(start); err != nil {
-		return errors.Wrapf(err, "release name %q", start)
+		return fmt.Errorf("release name %q: %w", start, err)
 	}
 	// On dry run, bail here
 	if i.isDryRun() {
@@ -653,7 +654,7 @@ func createOrOpenFile(filename string, appendData bool) (*os.File, error) {
 func ensureDirectoryForFile(file string) error {
 	baseDir := path.Dir(file)
 	_, err := os.Stat(baseDir)
-	if err != nil && !os.IsNotExist(err) {
+	if err != nil && !errors.Is(err, fs.ErrNotExist) {
 		return err
 	}
 
@@ -675,7 +676,7 @@ func (i *Install) NameAndChart(args []string) (string, string, error) {
 	}
 
 	if len(args) > 2 {
-		return args[0], args[1], errors.Errorf("expected at most two arguments, unexpected arguments: %v", strings.Join(args[2:], ", "))
+		return args[0], args[1], fmt.Errorf("expected at most two arguments, unexpected arguments: %v", strings.Join(args[2:], ", "))
 	}
 
 	if len(args) == 2 {
@@ -740,7 +741,7 @@ OUTER:
 	}
 
 	if len(missing) > 0 {
-		return errors.Errorf("found in Chart.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", "))
+		return fmt.Errorf("found in Chart.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", "))
 	}
 	return nil
 }
@@ -776,7 +777,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
 		return abs, nil
 	}
 	if filepath.IsAbs(name) || strings.HasPrefix(name, ".") {
-		return name, errors.Errorf("path %q not found", name)
+		return name, fmt.Errorf("path %q not found", name)
 	}
 
 	dl := downloader.ChartDownloader{
diff --git a/pkg/action/lint.go b/pkg/action/lint.go
index 451eb65b0..7b3c00ad2 100644
--- a/pkg/action/lint.go
+++ b/pkg/action/lint.go
@@ -17,12 +17,11 @@ limitations under the License.
 package action
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
 	"helm.sh/helm/v4/pkg/lint"
 	"helm.sh/helm/v4/pkg/lint/support"
@@ -94,26 +93,26 @@ func lintChart(path string, vals map[string]interface{}, namespace string, kubeV
 	if strings.HasSuffix(path, ".tgz") || strings.HasSuffix(path, ".tar.gz") {
 		tempDir, err := os.MkdirTemp("", "helm-lint")
 		if err != nil {
-			return linter, errors.Wrap(err, "unable to create temp dir to extract tarball")
+			return linter, fmt.Errorf("unable to create temp dir to extract tarball: %w", err)
 		}
 		defer os.RemoveAll(tempDir)
 
 		file, err := os.Open(path)
 		if err != nil {
-			return linter, errors.Wrap(err, "unable to open tarball")
+			return linter, fmt.Errorf("unable to open tarball: %w", err)
 		}
 		defer file.Close()
 
 		if err = chartutil.Expand(tempDir, file); err != nil {
-			return linter, errors.Wrap(err, "unable to extract tarball")
+			return linter, fmt.Errorf("unable to extract tarball: %w", err)
 		}
 
 		files, err := os.ReadDir(tempDir)
 		if err != nil {
-			return linter, errors.Wrapf(err, "unable to read temporary output directory %s", tempDir)
+			return linter, fmt.Errorf("unable to read temporary output directory %s: %w", tempDir, err)
 		}
 		if !files[0].IsDir() {
-			return linter, errors.Errorf("unexpected file %s in temporary output directory %s", files[0].Name(), tempDir)
+			return linter, fmt.Errorf("unexpected file %s in temporary output directory %s", files[0].Name(), tempDir)
 		}
 
 		chartPath = filepath.Join(tempDir, files[0].Name())
@@ -123,7 +122,7 @@ func lintChart(path string, vals map[string]interface{}, namespace string, kubeV
 
 	// Guard: Error out if this is not a chart.
 	if _, err := os.Stat(filepath.Join(chartPath, "Chart.yaml")); err != nil {
-		return linter, errors.Wrap(err, "unable to check Chart.yaml file in chart")
+		return linter, fmt.Errorf("unable to check Chart.yaml file in chart: %w", err)
 	}
 
 	return lint.RunAll(
diff --git a/pkg/action/package.go b/pkg/action/package.go
index 8f37779e6..e57ce4921 100644
--- a/pkg/action/package.go
+++ b/pkg/action/package.go
@@ -18,12 +18,12 @@ package action
 
 import (
 	"bufio"
+	"errors"
 	"fmt"
 	"os"
 	"syscall"
 
 	"github.com/Masterminds/semver/v3"
-	"github.com/pkg/errors"
 	"golang.org/x/term"
 
 	"helm.sh/helm/v4/pkg/chart/v2/loader"
@@ -105,7 +105,7 @@ func (p *Package) Run(path string, _ map[string]interface{}) (string, error) {
 
 	name, err := chartutil.Save(ch, dest)
 	if err != nil {
-		return "", errors.Wrap(err, "failed to save")
+		return "", fmt.Errorf("failed to save: %w", err)
 	}
 
 	if p.Sign {
diff --git a/pkg/action/pull.go b/pkg/action/pull.go
index eb208ca7b..a2f53af0d 100644
--- a/pkg/action/pull.go
+++ b/pkg/action/pull.go
@@ -22,8 +22,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
 	"helm.sh/helm/v4/pkg/cli"
 	"helm.sh/helm/v4/pkg/downloader"
@@ -111,7 +109,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
 		var err error
 		dest, err = os.MkdirTemp("", "helm-")
 		if err != nil {
-			return out.String(), errors.Wrap(err, "failed to untar")
+			return out.String(), fmt.Errorf("failed to untar: %w", err)
 		}
 		defer os.RemoveAll(dest)
 	}
@@ -163,11 +161,10 @@ func (p *Pull) Run(chartRef string) (string, error) {
 
 		if _, err := os.Stat(udCheck); err != nil {
 			if err := os.MkdirAll(udCheck, 0755); err != nil {
-				return out.String(), errors.Wrap(err, "failed to untar (mkdir)")
+				return out.String(), fmt.Errorf("failed to untar (mkdir): %w", err)
 			}
-
 		} else {
-			return out.String(), errors.Errorf("failed to untar: a file or directory with the name %s already exists", udCheck)
+			return out.String(), fmt.Errorf("failed to untar: a file or directory with the name %s already exists", udCheck)
 		}
 
 		return out.String(), chartutil.ExpandFile(ud, saved)
diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go
index 34bd0ac52..ac8a28fe0 100644
--- a/pkg/action/rollback.go
+++ b/pkg/action/rollback.go
@@ -23,8 +23,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/pkg/errors"
-
 	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
 	"helm.sh/helm/v4/pkg/kube"
 	release "helm.sh/helm/v4/pkg/release/v1"
@@ -95,7 +93,7 @@ func (r *Rollback) Run(name string) error {
 // the previous release's configuration
 func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Release, error) {
 	if err := chartutil.ValidateReleaseName(name); err != nil {
-		return nil, nil, errors.Errorf("prepareRollback: Release name is invalid: %s", name)
+		return nil, nil, fmt.Errorf("prepareRollback: Release name is invalid: %s", name)
 	}
 
 	if r.Version < 0 {
@@ -127,7 +125,7 @@ func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Rele
 		}
 	}
 	if !previousVersionExist {
-		return nil, nil, errors.Errorf("release has no %d version", previousVersion)
+		return nil, nil, fmt.Errorf("release has no %d version", previousVersion)
 	}
 
 	slog.Debug("rolling back", "name", name, "currentVersion", currentRelease.Version, "targetVersion", previousVersion)
@@ -169,11 +167,11 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
 
 	current, err := r.cfg.KubeClient.Build(bytes.NewBufferString(currentRelease.Manifest), false)
 	if err != nil {
-		return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest")
+		return targetRelease, fmt.Errorf("unable to build kubernetes objects from current release manifest: %w", err)
 	}
 	target, err := r.cfg.KubeClient.Build(bytes.NewBufferString(targetRelease.Manifest), false)
 	if err != nil {
-		return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest")
+		return targetRelease, fmt.Errorf("unable to build kubernetes objects from new release manifest: %w", err)
 	}
 
 	// pre-rollback hooks
@@ -188,7 +186,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
 	// It is safe to use "force" here because these are resources currently rendered by the chart.
 	err = target.Visit(setMetadataVisitor(targetRelease.Name, targetRelease.Namespace, true))
 	if err != nil {
-		return targetRelease, errors.Wrap(err, "unable to set metadata visitor from target release")
+		return targetRelease, fmt.Errorf("unable to set metadata visitor from target release: %w", err)
 	}
 	results, err := r.cfg.KubeClient.Update(current, target, r.Force)
 
@@ -204,11 +202,9 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
 			slog.Debug("cleanup on fail set, cleaning up resources", "count", len(results.Created))
 			_, errs := r.cfg.KubeClient.Delete(results.Created)
 			if errs != nil {
-				var errorList []string
-				for _, e := range errs {
-					errorList = append(errorList, e.Error())
-				}
-				return targetRelease, errors.Wrapf(fmt.Errorf("unable to cleanup resources: %s", strings.Join(errorList, ", ")), "an error occurred while cleaning up resources. original rollback error: %s", err)
+				return targetRelease, fmt.Errorf(
+					"an error occurred while cleaning up resources. original rollback error: %w",
+					fmt.Errorf("unable to cleanup resources: %w", joinErrors(errs, ", ")))
 			}
 			slog.Debug("resource cleanup complete")
 		}
@@ -226,21 +222,21 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
 	}
 	waiter, err := r.cfg.KubeClient.GetWaiter(r.WaitStrategy)
 	if err != nil {
-		return nil, errors.Wrap(err, "unable to set metadata visitor from target release")
+		return nil, fmt.Errorf("unable to set metadata visitor from target release: %w", err)
 	}
 	if r.WaitForJobs {
 		if err := waiter.WaitWithJobs(target, r.Timeout); err != nil {
 			targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
 			r.cfg.recordRelease(currentRelease)
 			r.cfg.recordRelease(targetRelease)
-			return targetRelease, errors.Wrapf(err, "release %s failed", targetRelease.Name)
+			return targetRelease, fmt.Errorf("release %s failed: %w", targetRelease.Name, err)
 		}
 	} else {
 		if err := waiter.Wait(target, r.Timeout); err != nil {
 			targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
 			r.cfg.recordRelease(currentRelease)
 			r.cfg.recordRelease(targetRelease)
-			return targetRelease, errors.Wrapf(err, "release %s failed", targetRelease.Name)
+			return targetRelease, fmt.Errorf("release %s failed: %w", targetRelease.Name, err)
 		}
 	}
 
diff --git a/pkg/action/show.go b/pkg/action/show.go
index f9843941b..a3fbcfa9e 100644
--- a/pkg/action/show.go
+++ b/pkg/action/show.go
@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/pkg/errors"
 	"k8s.io/cli-runtime/pkg/printers"
 	"sigs.k8s.io/yaml"
 
@@ -105,7 +104,7 @@ func (s *Show) Run(chartpath string) (string, error) {
 		if s.JSONPathTemplate != "" {
 			printer, err := printers.NewJSONPathPrinter(s.JSONPathTemplate)
 			if err != nil {
-				return "", errors.Wrapf(err, "error parsing jsonpath %s", s.JSONPathTemplate)
+				return "", fmt.Errorf("error parsing jsonpath %s: %w", s.JSONPathTemplate, err)
 			}
 			printer.Execute(&out, s.chart.Values)
 		} else {
diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go
index fa69d2a48..61e10b2c8 100644
--- a/pkg/action/uninstall.go
+++ b/pkg/action/uninstall.go
@@ -17,12 +17,11 @@ limitations under the License.
 package action
 
 import (
+	"fmt"
 	"log/slog"
 	"strings"
 	"time"
 
-	"github.com/pkg/errors"
-
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
@@ -76,7 +75,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
 	}
 
 	if err := chartutil.ValidateReleaseName(name); err != nil {
-		return nil, errors.Errorf("uninstall: Release name is invalid: %s", name)
+		return nil, fmt.Errorf("uninstall: Release name is invalid: %s", name)
 	}
 
 	rels, err := u.cfg.Releases.History(name)
@@ -84,7 +83,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
 		if u.IgnoreNotFound {
 			return nil, nil
 		}
-		return nil, errors.Wrapf(err, "uninstall: Release not loaded: %s", name)
+		return nil, fmt.Errorf("uninstall: Release not loaded: %s: %w", name, err)
 	}
 	if len(rels) < 1 {
 		return nil, errMissingRelease
@@ -98,11 +97,11 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
 	if rel.Info.Status == release.StatusUninstalled {
 		if !u.KeepHistory {
 			if err := u.purgeReleases(rels...); err != nil {
-				return nil, errors.Wrap(err, "uninstall: Failed to purge the release")
+				return nil, fmt.Errorf("uninstall: Failed to purge the release: %w", err)
 			}
 			return &release.UninstallReleaseResponse{Release: rel}, nil
 		}
-		return nil, errors.Errorf("the release named %q is already deleted", name)
+		return nil, fmt.Errorf("the release named %q is already deleted", name)
 	}
 
 	slog.Debug("uninstall: deleting release", "name", name)
@@ -128,7 +127,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
 	deletedResources, kept, errs := u.deleteRelease(rel)
 	if errs != nil {
 		slog.Debug("uninstall: Failed to delete release", slog.Any("error", errs))
-		return nil, errors.Errorf("failed to delete release: %s", name)
+		return nil, fmt.Errorf("failed to delete release: %s", name)
 	}
 
 	if kept != "" {
@@ -157,12 +156,12 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
 		slog.Debug("purge requested", "release", name)
 		err := u.purgeReleases(rels...)
 		if err != nil {
-			errs = append(errs, errors.Wrap(err, "uninstall: Failed to purge the release"))
+			errs = append(errs, fmt.Errorf("uninstall: Failed to purge the release: %w", err))
 		}
 
 		// Return the errors that occurred while deleting the release, if any
 		if len(errs) > 0 {
-			return res, errors.Errorf("uninstallation completed with %d error(s): %s", len(errs), joinErrors(errs))
+			return res, fmt.Errorf("uninstallation completed with %d error(s): %w", len(errs), joinErrors(errs, "; "))
 		}
 
 		return res, nil
@@ -173,7 +172,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
 	}
 
 	if len(errs) > 0 {
-		return res, errors.Errorf("uninstallation completed with %d error(s): %s", len(errs), joinErrors(errs))
+		return res, fmt.Errorf("uninstallation completed with %d error(s): %w", len(errs), joinErrors(errs, "; "))
 	}
 	return res, nil
 }
@@ -187,12 +186,28 @@ func (u *Uninstall) purgeReleases(rels ...*release.Release) error {
 	return nil
 }
 
-func joinErrors(errs []error) string {
-	es := make([]string, 0, len(errs))
-	for _, e := range errs {
-		es = append(es, e.Error())
+type joinedErrors struct {
+	errs []error
+	sep  string
+}
+
+func joinErrors(errs []error, sep string) error {
+	return &joinedErrors{
+		errs: errs,
+		sep:  sep,
+	}
+}
+
+func (e *joinedErrors) Error() string {
+	errs := make([]string, 0, len(e.errs))
+	for _, err := range e.errs {
+		errs = append(errs, err.Error())
 	}
-	return strings.Join(es, "; ")
+	return strings.Join(errs, e.sep)
+}
+
+func (e *joinedErrors) Unwrap() []error {
+	return e.errs
 }
 
 // deleteRelease deletes the release and returns list of delete resources and manifests that were kept in the deletion process
@@ -206,7 +221,7 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (kube.ResourceList, stri
 		// FIXME: One way to delete at this point would be to try a label-based
 		// deletion. The problem with this is that we could get a false positive
 		// and delete something that was not legitimately part of this release.
-		return nil, rel.Manifest, []error{errors.Wrap(err, "corrupted release record. You must manually delete the resources")}
+		return nil, rel.Manifest, []error{fmt.Errorf("corrupted release record. You must manually delete the resources: %w", err)}
 	}
 
 	filesToKeep, filesToDelete := filterManifestsToKeep(files)
@@ -222,7 +237,7 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (kube.ResourceList, stri
 
 	resources, err := u.cfg.KubeClient.Build(strings.NewReader(builder.String()), false)
 	if err != nil {
-		return nil, "", []error{errors.Wrap(err, "unable to build kubernetes objects for delete")}
+		return nil, "", []error{fmt.Errorf("unable to build kubernetes objects for delete: %w", err)}
 	}
 	if len(resources) > 0 {
 		if kubeClient, ok := u.cfg.KubeClient.(kube.InterfaceDeletionPropagation); ok {
diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go
index b32bf256e..e2d2ead69 100644
--- a/pkg/action/upgrade.go
+++ b/pkg/action/upgrade.go
@@ -19,13 +19,13 @@ package action
 import (
 	"bytes"
 	"context"
+	"errors"
 	"fmt"
 	"log/slog"
 	"strings"
 	"sync"
 	"time"
 
-	"github.com/pkg/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/cli-runtime/pkg/resource"
 
@@ -161,7 +161,7 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
 	}
 
 	if err := chartutil.ValidateReleaseName(name); err != nil {
-		return nil, errors.Errorf("release name is invalid: %s", name)
+		return nil, fmt.Errorf("release name is invalid: %s", name)
 	}
 
 	slog.Debug("preparing upgrade", "name", name)
@@ -205,7 +205,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
 
 	// HideSecret must be used with dry run. Otherwise, return an error.
 	if !u.isDryRun() && u.HideSecret {
-		return nil, nil, errors.New("Hiding Kubernetes secrets requires a dry-run mode")
+		return nil, nil, errors.New("hiding Kubernetes secrets requires a dry-run mode")
 	}
 
 	// finds the last non-deleted release with the given name
@@ -316,15 +316,15 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
 		// Checking for removed Kubernetes API error so can provide a more informative error message to the user
 		// Ref: https://github.com/helm/helm/issues/7219
 		if strings.Contains(err.Error(), "unable to recognize \"\": no matches for kind") {
-			return upgradedRelease, errors.Wrap(err, "current release manifest contains removed kubernetes api(s) for this "+
+			return upgradedRelease, fmt.Errorf("current release manifest contains removed kubernetes api(s) for this "+
 				"kubernetes version and it is therefore unable to build the kubernetes "+
-				"objects for performing the diff. error from kubernetes")
+				"objects for performing the diff. error from kubernetes: %w", err)
 		}
-		return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest")
+		return upgradedRelease, fmt.Errorf("unable to build kubernetes objects from current release manifest: %w", err)
 	}
 	target, err := u.cfg.KubeClient.Build(bytes.NewBufferString(upgradedRelease.Manifest), !u.DisableOpenAPIValidation)
 	if err != nil {
-		return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest")
+		return upgradedRelease, fmt.Errorf("unable to build kubernetes objects from new release manifest: %w", err)
 	}
 
 	// It is safe to use force only on target because these are resources currently rendered by the chart.
@@ -353,7 +353,7 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
 		toBeUpdated, err = existingResourceConflict(toBeCreated, upgradedRelease.Name, upgradedRelease.Namespace)
 	}
 	if err != nil {
-		return nil, errors.Wrap(err, "Unable to continue with update")
+		return nil, fmt.Errorf("unable to continue with update: %w", err)
 	}
 
 	toBeUpdated.Visit(func(r *resource.Info, err error) error {
@@ -496,11 +496,14 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
 		slog.Debug("cleanup on fail set", "cleaning_resources", len(created))
 		_, errs := u.cfg.KubeClient.Delete(created)
 		if errs != nil {
-			var errorList []string
-			for _, e := range errs {
-				errorList = append(errorList, e.Error())
-			}
-			return rel, errors.Wrapf(fmt.Errorf("unable to cleanup resources: %s", strings.Join(errorList, ", ")), "an error occurred while cleaning up resources. original upgrade error: %s", err)
+			return rel, fmt.Errorf(
+				"an error occurred while cleaning up resources. original upgrade error: %w: %w",
+				err,
+				fmt.Errorf(
+					"unable to cleanup resources: %w",
+					joinErrors(errs, ", "),
+				),
+			)
 		}
 		slog.Debug("resource cleanup complete")
 	}
@@ -512,7 +515,7 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
 		hist := NewHistory(u.cfg)
 		fullHistory, herr := hist.Run(rel.Name)
 		if herr != nil {
-			return rel, errors.Wrapf(herr, "an error occurred while finding last successful release. original upgrade error: %s", err)
+			return rel, fmt.Errorf("an error occurred while finding last successful release. original upgrade error: %w: %w", err, herr)
 		}
 
 		// There isn't a way to tell if a previous release was successful, but
@@ -522,7 +525,7 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
 			return r.Info.Status == release.StatusSuperseded || r.Info.Status == release.StatusDeployed
 		}).Filter(fullHistory)
 		if len(filteredHistory) == 0 {
-			return rel, errors.Wrap(err, "unable to find a previously successful release when attempting to rollback. original upgrade error")
+			return rel, fmt.Errorf("unable to find a previously successful release when attempting to rollback. original upgrade error: %w", err)
 		}
 
 		releaseutil.Reverse(filteredHistory, releaseutil.SortByRevision)
@@ -538,9 +541,9 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
 		rollin.Force = u.Force
 		rollin.Timeout = u.Timeout
 		if rollErr := rollin.Run(rel.Name); rollErr != nil {
-			return rel, errors.Wrapf(rollErr, "an error occurred while rolling back the release. original upgrade error: %s", err)
+			return rel, fmt.Errorf("an error occurred while rolling back the release. original upgrade error: %w: %w", err, rollErr)
 		}
-		return rel, errors.Wrapf(err, "release %s failed, and has been rolled back due to atomic being set", rel.Name)
+		return rel, fmt.Errorf("release %s failed, and has been rolled back due to atomic being set: %w", rel.Name, err)
 	}
 
 	return rel, err
@@ -568,7 +571,7 @@ func (u *Upgrade) reuseValues(chart *chart.Chart, current *release.Release, newV
 		// We have to regenerate the old coalesced values:
 		oldVals, err := chartutil.CoalesceValues(current.Chart, current.Config)
 		if err != nil {
-			return nil, errors.Wrap(err, "failed to rebuild old values")
+			return nil, fmt.Errorf("failed to rebuild old values: %w", err)
 		}
 
 		newVals = chartutil.CoalesceTables(newVals, current.Config)
@@ -614,21 +617,21 @@ func recreate(cfg *Configuration, resources kube.ResourceList) error {
 
 		client, err := cfg.KubernetesClientSet()
 		if err != nil {
-			return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name)
+			return fmt.Errorf("unable to recreate pods for object %s/%s because an error occurred: %w", res.Namespace, res.Name, err)
 		}
 
 		pods, err := client.CoreV1().Pods(res.Namespace).List(context.Background(), metav1.ListOptions{
 			LabelSelector: selector.String(),
 		})
 		if err != nil {
-			return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name)
+			return fmt.Errorf("unable to recreate pods for object %s/%s because an error occurred: %w", res.Namespace, res.Name, err)
 		}
 
 		// Restart pods
 		for _, pod := range pods.Items {
 			// Delete each pod for get them restarted with changed spec.
 			if err := client.CoreV1().Pods(pod.Namespace).Delete(context.Background(), pod.Name, *metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil {
-				return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name)
+				return fmt.Errorf("unable to recreate pods for object %s/%s because an error occurred: %w", res.Namespace, res.Name, err)
 			}
 		}
 	}
diff --git a/pkg/action/validate.go b/pkg/action/validate.go
index 4bc70e90c..22db74041 100644
--- a/pkg/action/validate.go
+++ b/pkg/action/validate.go
@@ -19,7 +19,6 @@ package action
 import (
 	"fmt"
 
-	"github.com/pkg/errors"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -52,7 +51,7 @@ func requireAdoption(resources kube.ResourceList) (kube.ResourceList, error) {
 			if apierrors.IsNotFound(err) {
 				return nil
 			}
-			return errors.Wrapf(err, "could not get information about the resource %s", resourceString(info))
+			return fmt.Errorf("could not get information about the resource %s: %w", resourceString(info), err)
 		}
 
 		requireUpdate.Append(info)
@@ -76,7 +75,7 @@ func existingResourceConflict(resources kube.ResourceList, releaseName, releaseN
 			if apierrors.IsNotFound(err) {
 				return nil
 			}
-			return errors.Wrapf(err, "could not get information about the resource %s", resourceString(info))
+			return fmt.Errorf("could not get information about the resource %s: %w", resourceString(info), err)
 		}
 
 		// Allow adoption of the resource if it is managed by Helm and is annotated with correct release name and namespace.
@@ -113,11 +112,7 @@ func checkOwnership(obj runtime.Object, releaseName, releaseNamespace string) er
 	}
 
 	if len(errs) > 0 {
-		err := errors.New("invalid ownership metadata")
-		for _, e := range errs {
-			err = fmt.Errorf("%w; %s", err, e)
-		}
-		return err
+		return fmt.Errorf("invalid ownership metadata; %w", joinErrors(errs, "; "))
 	}
 
 	return nil
diff --git a/pkg/chart/v2/loader/archive.go b/pkg/chart/v2/loader/archive.go
index 655fe87fa..b9f370f56 100644
--- a/pkg/chart/v2/loader/archive.go
+++ b/pkg/chart/v2/loader/archive.go
@@ -20,6 +20,7 @@ import (
 	"archive/tar"
 	"bytes"
 	"compress/gzip"
+	"errors"
 	"fmt"
 	"io"
 	"net/http"
@@ -28,8 +29,6 @@ import (
 	"regexp"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	chart "helm.sh/helm/v4/pkg/chart/v2"
 )
 
@@ -170,7 +169,7 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
 		n = path.Clean(n)
 		if n == "." {
 			// In this case, the original path was relative when it should have been absolute.
-			return nil, errors.Errorf("chart illegally contains content outside the base directory: %q", hd.Name)
+			return nil, fmt.Errorf("chart illegally contains content outside the base directory: %q", hd.Name)
 		}
 		if strings.HasPrefix(n, "..") {
 			return nil, errors.New("chart illegally references parent directory")
diff --git a/pkg/chart/v2/loader/directory.go b/pkg/chart/v2/loader/directory.go
index dbf3eb882..4f72925dc 100644
--- a/pkg/chart/v2/loader/directory.go
+++ b/pkg/chart/v2/loader/directory.go
@@ -23,8 +23,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/internal/sympath"
 	chart "helm.sh/helm/v4/pkg/chart/v2"
 	"helm.sh/helm/v4/pkg/ignore"
@@ -107,7 +105,7 @@ func LoadDir(dir string) (*chart.Chart, error) {
 
 		data, err := os.ReadFile(name)
 		if err != nil {
-			return errors.Wrapf(err, "error reading %s", n)
+			return fmt.Errorf("error reading %s: %w", n, err)
 		}
 
 		data = bytes.TrimPrefix(data, utf8bom)
diff --git a/pkg/chart/v2/loader/load.go b/pkg/chart/v2/loader/load.go
index 3c5463720..7838b577f 100644
--- a/pkg/chart/v2/loader/load.go
+++ b/pkg/chart/v2/loader/load.go
@@ -20,13 +20,14 @@ import (
 	"bufio"
 	"bytes"
 	"encoding/json"
+	"errors"
+	"fmt"
 	"io"
 	"log"
 	"os"
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
 	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
 	"sigs.k8s.io/yaml"
 
@@ -48,7 +49,6 @@ func Loader(name string) (ChartLoader, error) {
 		return DirLoader(name), nil
 	}
 	return FileLoader(name), nil
-
 }
 
 // Load takes a string name, tries to resolve it to a file or directory, and then loads it.
@@ -86,7 +86,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
 				c.Metadata = new(chart.Metadata)
 			}
 			if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil {
-				return c, errors.Wrap(err, "cannot load Chart.yaml")
+				return c, fmt.Errorf("cannot load Chart.yaml: %w", err)
 			}
 			// NOTE(bacongobbler): while the chart specification says that APIVersion must be set,
 			// Helm 2 accepted charts that did not provide an APIVersion in their chart metadata.
@@ -104,12 +104,12 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
 		case f.Name == "Chart.lock":
 			c.Lock = new(chart.Lock)
 			if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil {
-				return c, errors.Wrap(err, "cannot load Chart.lock")
+				return c, fmt.Errorf("cannot load Chart.lock: %w", err)
 			}
 		case f.Name == "values.yaml":
 			values, err := LoadValues(bytes.NewReader(f.Data))
 			if err != nil {
-				return c, errors.Wrap(err, "cannot load values.yaml")
+				return c, fmt.Errorf("cannot load values.yaml: %w", err)
 			}
 			c.Values = values
 		case f.Name == "values.schema.json":
@@ -125,7 +125,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
 				log.Printf("Warning: Dependencies are handled in Chart.yaml since apiVersion \"v2\". We recommend migrating dependencies to Chart.yaml.")
 			}
 			if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil {
-				return c, errors.Wrap(err, "cannot load requirements.yaml")
+				return c, fmt.Errorf("cannot load requirements.yaml: %w", err)
 			}
 			if c.Metadata.APIVersion == chart.APIVersionV1 {
 				c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
@@ -134,7 +134,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
 		case f.Name == "requirements.lock":
 			c.Lock = new(chart.Lock)
 			if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil {
-				return c, errors.Wrap(err, "cannot load requirements.lock")
+				return c, fmt.Errorf("cannot load requirements.lock: %w", err)
 			}
 			if c.Metadata == nil {
 				c.Metadata = new(chart.Metadata)
@@ -163,7 +163,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
 	}
 
 	if c.Metadata == nil {
-		return c, errors.New("Chart.yaml file is missing")
+		return c, errors.New("Chart.yaml file is missing") //nolint:staticcheck
 	}
 
 	if err := c.Validate(); err != nil {
@@ -179,7 +179,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
 		case filepath.Ext(n) == ".tgz":
 			file := files[0]
 			if file.Name != n {
-				return c, errors.Errorf("error unpacking subchart tar in %s: expected %s, got %s", c.Name(), n, file.Name)
+				return c, fmt.Errorf("error unpacking subchart tar in %s: expected %s, got %s", c.Name(), n, file.Name)
 			}
 			// Untar the chart and add to c.Dependencies
 			sc, err = LoadArchive(bytes.NewBuffer(file.Data))
@@ -199,7 +199,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
 		}
 
 		if err != nil {
-			return c, errors.Wrapf(err, "error unpacking subchart %s in %s", n, c.Name())
+			return c, fmt.Errorf("error unpacking subchart %s in %s: %w", n, c.Name(), err)
 		}
 		c.AddDependency(sc)
 	}
@@ -221,13 +221,13 @@ func LoadValues(data io.Reader) (map[string]interface{}, error) {
 			if err == io.EOF {
 				break
 			}
-			return nil, errors.Wrap(err, "error reading yaml document")
+			return nil, fmt.Errorf("error reading yaml document: %w", err)
 		}
 		if err := yaml.Unmarshal(raw, &currentMap, func(d *json.Decoder) *json.Decoder {
 			d.UseNumber()
 			return d
 		}); err != nil {
-			return nil, errors.Wrap(err, "cannot unmarshal yaml document")
+			return nil, fmt.Errorf("cannot unmarshal yaml document: %w", err)
 		}
 		values = MergeMaps(values, currentMap)
 	}
diff --git a/pkg/chart/v2/util/chartfile.go b/pkg/chart/v2/util/chartfile.go
index b48687d55..6748c6a91 100644
--- a/pkg/chart/v2/util/chartfile.go
+++ b/pkg/chart/v2/util/chartfile.go
@@ -17,10 +17,12 @@ limitations under the License.
 package util
 
 import (
+	"errors"
+	"fmt"
+	"io/fs"
 	"os"
 	"path/filepath"
 
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
@@ -75,17 +77,17 @@ func IsChartDir(dirName string) (bool, error) {
 	if fi, err := os.Stat(dirName); err != nil {
 		return false, err
 	} else if !fi.IsDir() {
-		return false, errors.Errorf("%q is not a directory", dirName)
+		return false, fmt.Errorf("%q is not a directory", dirName)
 	}
 
 	chartYaml := filepath.Join(dirName, ChartfileName)
-	if _, err := os.Stat(chartYaml); os.IsNotExist(err) {
-		return false, errors.Errorf("no %s exists in directory %q", ChartfileName, dirName)
+	if _, err := os.Stat(chartYaml); errors.Is(err, fs.ErrNotExist) {
+		return false, fmt.Errorf("no %s exists in directory %q", ChartfileName, dirName)
 	}
 
 	chartYamlContent, err := os.ReadFile(chartYaml)
 	if err != nil {
-		return false, errors.Errorf("cannot read %s in directory %q", ChartfileName, dirName)
+		return false, fmt.Errorf("cannot read %s in directory %q", ChartfileName, dirName)
 	}
 
 	chartContent := new(chart.Metadata)
@@ -93,10 +95,10 @@ func IsChartDir(dirName string) (bool, error) {
 		return false, err
 	}
 	if chartContent == nil {
-		return false, errors.Errorf("chart metadata (%s) missing", ChartfileName)
+		return false, fmt.Errorf("chart metadata (%s) missing", ChartfileName)
 	}
 	if chartContent.Name == "" {
-		return false, errors.Errorf("invalid chart (%s): name must not be empty", ChartfileName)
+		return false, fmt.Errorf("invalid chart (%s): name must not be empty", ChartfileName)
 	}
 
 	return true, nil
diff --git a/pkg/chart/v2/util/coalesce.go b/pkg/chart/v2/util/coalesce.go
index b986273a9..76dfdfa1a 100644
--- a/pkg/chart/v2/util/coalesce.go
+++ b/pkg/chart/v2/util/coalesce.go
@@ -21,7 +21,6 @@ import (
 	"log"
 
 	"github.com/mitchellh/copystructure"
-	"github.com/pkg/errors"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
 )
@@ -108,7 +107,7 @@ func coalesceDeps(printf printFn, chrt *chart.Chart, dest map[string]interface{}
 			// If dest doesn't already have the key, create it.
 			dest[subchart.Name()] = make(map[string]interface{})
 		} else if !istable(c) {
-			return dest, errors.Errorf("type mismatch on %s: %t", subchart.Name(), c)
+			return dest, fmt.Errorf("type mismatch on %s: %t", subchart.Name(), c)
 		}
 		if dv, ok := dest[subchart.Name()]; ok {
 			dvmap := dv.(map[string]interface{})
diff --git a/pkg/chart/v2/util/create.go b/pkg/chart/v2/util/create.go
index 35a8c64a0..93699e0fd 100644
--- a/pkg/chart/v2/util/create.go
+++ b/pkg/chart/v2/util/create.go
@@ -24,7 +24,6 @@ import (
 	"regexp"
 	"strings"
 
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
@@ -651,7 +650,7 @@ var Stderr io.Writer = os.Stderr
 func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
 	schart, err := loader.Load(src)
 	if err != nil {
-		return errors.Wrapf(err, "could not load %s", src)
+		return fmt.Errorf("could not load %s: %w", src, err)
 	}
 
 	schart.Metadata = chartfile
@@ -666,12 +665,12 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
 	schart.Templates = updatedTemplates
 	b, err := yaml.Marshal(schart.Values)
 	if err != nil {
-		return errors.Wrap(err, "reading values file")
+		return fmt.Errorf("reading values file: %w", err)
 	}
 
 	var m map[string]interface{}
 	if err := yaml.Unmarshal(transform(string(b), schart.Name()), &m); err != nil {
-		return errors.Wrap(err, "transforming values file")
+		return fmt.Errorf("transforming values file: %w", err)
 	}
 	schart.Values = m
 
@@ -715,12 +714,12 @@ func Create(name, dir string) (string, error) {
 	if fi, err := os.Stat(path); err != nil {
 		return path, err
 	} else if !fi.IsDir() {
-		return path, errors.Errorf("no such directory %s", path)
+		return path, fmt.Errorf("no such directory %s", path)
 	}
 
 	cdir := filepath.Join(path, name)
 	if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() {
-		return cdir, errors.Errorf("file %s already exists and is not a directory", cdir)
+		return cdir, fmt.Errorf("file %s already exists and is not a directory", cdir)
 	}
 
 	// Note: If adding a new template below (i.e., to `helm create`) which is disabled by default (similar to hpa and
diff --git a/pkg/chart/v2/util/expand.go b/pkg/chart/v2/util/expand.go
index e05a1a984..9d08571ed 100644
--- a/pkg/chart/v2/util/expand.go
+++ b/pkg/chart/v2/util/expand.go
@@ -17,12 +17,13 @@ limitations under the License.
 package util
 
 import (
+	"errors"
+	"fmt"
 	"io"
 	"os"
 	"path/filepath"
 
 	securejoin "github.com/cyphar/filepath-securejoin"
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
@@ -42,7 +43,7 @@ func Expand(dir string, r io.Reader) error {
 		if file.Name == "Chart.yaml" {
 			ch := &chart.Metadata{}
 			if err := yaml.Unmarshal(file.Data, ch); err != nil {
-				return errors.Wrap(err, "cannot load Chart.yaml")
+				return fmt.Errorf("cannot load Chart.yaml: %w", err)
 			}
 			chartName = ch.Name
 		}
diff --git a/pkg/chart/v2/util/jsonschema.go b/pkg/chart/v2/util/jsonschema.go
index 3de941e0b..820e5953a 100644
--- a/pkg/chart/v2/util/jsonschema.go
+++ b/pkg/chart/v2/util/jsonschema.go
@@ -18,11 +18,11 @@ package util
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
 	"log/slog"
 	"strings"
 
-	"github.com/pkg/errors"
 	"github.com/santhosh-tekuri/jsonschema/v6"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
diff --git a/pkg/chart/v2/util/save.go b/pkg/chart/v2/util/save.go
index dfa10915f..624a5b562 100644
--- a/pkg/chart/v2/util/save.go
+++ b/pkg/chart/v2/util/save.go
@@ -20,12 +20,13 @@ import (
 	"archive/tar"
 	"compress/gzip"
 	"encoding/json"
+	"errors"
 	"fmt"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"time"
 
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
@@ -45,7 +46,7 @@ func SaveDir(c *chart.Chart, dest string) error {
 	}
 	outdir := filepath.Join(dest, c.Name())
 	if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() {
-		return errors.Errorf("file %s already exists and is not a directory", outdir)
+		return fmt.Errorf("file %s already exists and is not a directory", outdir)
 	}
 	if err := os.MkdirAll(outdir, 0755); err != nil {
 		return err
@@ -89,7 +90,7 @@ func SaveDir(c *chart.Chart, dest string) error {
 	for _, dep := range c.Dependencies() {
 		// Here, we write each dependency as a tar file.
 		if _, err := Save(dep, base); err != nil {
-			return errors.Wrapf(err, "saving %s", dep.ChartFullPath())
+			return fmt.Errorf("saving %s: %w", dep.ChartFullPath(), err)
 		}
 	}
 	return nil
@@ -105,22 +106,22 @@ func SaveDir(c *chart.Chart, dest string) error {
 // This returns the absolute path to the chart archive file.
 func Save(c *chart.Chart, outDir string) (string, error) {
 	if err := c.Validate(); err != nil {
-		return "", errors.Wrap(err, "chart validation")
+		return "", fmt.Errorf("chart validation: %w", err)
 	}
 
 	filename := fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version)
 	filename = filepath.Join(outDir, filename)
 	dir := filepath.Dir(filename)
 	if stat, err := os.Stat(dir); err != nil {
-		if os.IsNotExist(err) {
+		if errors.Is(err, fs.ErrNotExist) {
 			if err2 := os.MkdirAll(dir, 0755); err2 != nil {
 				return "", err2
 			}
 		} else {
-			return "", errors.Wrapf(err, "stat %s", dir)
+			return "", fmt.Errorf("stat %s: %w", dir, err)
 		}
 	} else if !stat.IsDir() {
-		return "", errors.Errorf("is not a directory: %s", dir)
+		return "", fmt.Errorf("is not a directory: %s", dir)
 	}
 
 	f, err := os.Create(filename)
@@ -203,7 +204,7 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
 	// Save values.schema.json if it exists
 	if c.Schema != nil {
 		if !json.Valid(c.Schema) {
-			return errors.New("Invalid JSON in " + SchemafileName)
+			return errors.New("invalid JSON in " + SchemafileName)
 		}
 		if err := writeToTar(out, filepath.Join(base, SchemafileName), c.Schema); err != nil {
 			return err
diff --git a/pkg/chart/v2/util/validate_name.go b/pkg/chart/v2/util/validate_name.go
index 73be43303..6595e085d 100644
--- a/pkg/chart/v2/util/validate_name.go
+++ b/pkg/chart/v2/util/validate_name.go
@@ -17,10 +17,9 @@ limitations under the License.
 package util
 
 import (
+	"errors"
 	"fmt"
 	"regexp"
-
-	"github.com/pkg/errors"
 )
 
 // validName is a regular expression for resource names.
diff --git a/pkg/chart/v2/util/values.go b/pkg/chart/v2/util/values.go
index 404ba9842..42b1a28e8 100644
--- a/pkg/chart/v2/util/values.go
+++ b/pkg/chart/v2/util/values.go
@@ -18,12 +18,12 @@ package util
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"os"
 	"strings"
 
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
@@ -169,8 +169,7 @@ func ToRenderValuesWithSchemaValidation(chrt *chart.Chart, chrtVals map[string]i
 
 	if !skipSchemaValidation {
 		if err := ValidateAgainstSchema(chrt, vals); err != nil {
-			errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s"
-			return top, fmt.Errorf(errFmt, err.Error())
+			return top, fmt.Errorf("values don't meet the specifications of the schema(s) in the following chart(s):\n%w", err)
 		}
 	}
 
diff --git a/pkg/cli/output/output.go b/pkg/cli/output/output.go
index 01649c812..28d503741 100644
--- a/pkg/cli/output/output.go
+++ b/pkg/cli/output/output.go
@@ -22,7 +22,6 @@ import (
 	"io"
 
 	"github.com/gosuri/uitable"
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 )
 
@@ -107,7 +106,7 @@ func EncodeJSON(out io.Writer, obj interface{}) error {
 	enc := json.NewEncoder(out)
 	err := enc.Encode(obj)
 	if err != nil {
-		return errors.Wrap(err, "unable to write JSON output")
+		return fmt.Errorf("unable to write JSON output: %w", err)
 	}
 	return nil
 }
@@ -117,12 +116,12 @@ func EncodeJSON(out io.Writer, obj interface{}) error {
 func EncodeYAML(out io.Writer, obj interface{}) error {
 	raw, err := yaml.Marshal(obj)
 	if err != nil {
-		return errors.Wrap(err, "unable to write YAML output")
+		return fmt.Errorf("unable to write YAML output: %w", err)
 	}
 
 	_, err = out.Write(raw)
 	if err != nil {
-		return errors.Wrap(err, "unable to write YAML output")
+		return fmt.Errorf("unable to write YAML output: %w", err)
 	}
 	return nil
 }
@@ -134,7 +133,7 @@ func EncodeTable(out io.Writer, table *uitable.Table) error {
 	raw = append(raw, []byte("\n")...)
 	_, err := out.Write(raw)
 	if err != nil {
-		return errors.Wrap(err, "unable to write table output")
+		return fmt.Errorf("unable to write table output: %w", err)
 	}
 	return nil
 }
diff --git a/pkg/cli/values/options.go b/pkg/cli/values/options.go
index 461db3cc2..cd65fa885 100644
--- a/pkg/cli/values/options.go
+++ b/pkg/cli/values/options.go
@@ -19,13 +19,12 @@ package values
 import (
 	"bytes"
 	"encoding/json"
+	"fmt"
 	"io"
 	"net/url"
 	"os"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/pkg/chart/v2/loader"
 	"helm.sh/helm/v4/pkg/getter"
 	"helm.sh/helm/v4/pkg/strvals"
@@ -54,7 +53,7 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er
 		}
 		currentMap, err := loader.LoadValues(bytes.NewReader(raw))
 		if err != nil {
-			return nil, errors.Wrapf(err, "failed to parse %s", filePath)
+			return nil, fmt.Errorf("failed to parse %s: %w", filePath, err)
 		}
 		// Merge with the previous map
 		base = loader.MergeMaps(base, currentMap)
@@ -67,13 +66,13 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er
 			// If value is JSON object format, parse it as map
 			var jsonMap map[string]interface{}
 			if err := json.Unmarshal([]byte(trimmedValue), &jsonMap); err != nil {
-				return nil, errors.Errorf("failed parsing --set-json data JSON: %s", value)
+				return nil, fmt.Errorf("failed parsing --set-json data JSON: %s", value)
 			}
 			base = loader.MergeMaps(base, jsonMap)
 		} else {
 			// Otherwise, parse it as key=value format
 			if err := strvals.ParseJSON(value, base); err != nil {
-				return nil, errors.Errorf("failed parsing --set-json data %s", value)
+				return nil, fmt.Errorf("failed parsing --set-json data %s", value)
 			}
 		}
 	}
@@ -81,14 +80,14 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er
 	// User specified a value via --set
 	for _, value := range opts.Values {
 		if err := strvals.ParseInto(value, base); err != nil {
-			return nil, errors.Wrap(err, "failed parsing --set data")
+			return nil, fmt.Errorf("failed parsing --set data: %w", err)
 		}
 	}
 
 	// User specified a value via --set-string
 	for _, value := range opts.StringValues {
 		if err := strvals.ParseIntoString(value, base); err != nil {
-			return nil, errors.Wrap(err, "failed parsing --set-string data")
+			return nil, fmt.Errorf("failed parsing --set-string data: %w", err)
 		}
 	}
 
@@ -102,14 +101,14 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er
 			return string(bytes), err
 		}
 		if err := strvals.ParseIntoFile(value, base, reader); err != nil {
-			return nil, errors.Wrap(err, "failed parsing --set-file data")
+			return nil, fmt.Errorf("failed parsing --set-file data: %w", err)
 		}
 	}
 
 	// User specified a value via --set-literal
 	for _, value := range opts.LiteralValues {
 		if err := strvals.ParseLiteralInto(value, base); err != nil {
-			return nil, errors.Wrap(err, "failed parsing --set-literal data")
+			return nil, fmt.Errorf("failed parsing --set-literal data: %w", err)
 		}
 	}
 
diff --git a/pkg/cmd/docs.go b/pkg/cmd/docs.go
index a22d96c4d..7fae60743 100644
--- a/pkg/cmd/docs.go
+++ b/pkg/cmd/docs.go
@@ -22,7 +22,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra/doc"
 	"golang.org/x/text/cases"
@@ -99,6 +98,6 @@ func (o *docsOptions) run(_ io.Writer) error {
 	case "bash":
 		return o.topCmd.GenBashCompletionFile(filepath.Join(o.dest, "completions.bash"))
 	default:
-		return errors.Errorf("unknown doc type %q. Try 'markdown' or 'man'", o.docTypeString)
+		return fmt.Errorf("unknown doc type %q. Try 'markdown' or 'man'", o.docTypeString)
 	}
 }
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index e35df7801..cbec33a80 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -18,6 +18,7 @@ package cmd
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"io"
 	"log"
@@ -27,7 +28,6 @@ import (
 	"syscall"
 	"time"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 
@@ -159,7 +159,7 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 			}
 			rel, err := runInstall(args, client, valueOpts, out)
 			if err != nil {
-				return errors.Wrap(err, "INSTALLATION FAILED")
+				return fmt.Errorf("INSTALLATION FAILED: %w", err)
 			}
 
 			return outfmt.Write(out, &statusPrinter{
@@ -274,7 +274,6 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
 		// As of Helm 2.4.0, this is treated as a stopping condition:
 		// https://github.com/helm/helm/issues/2209
 		if err := action.CheckDependencies(chartRequested, req); err != nil {
-			err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
 			if client.DependencyUpdate {
 				man := &downloader.Manager{
 					Out:              out,
@@ -292,10 +291,10 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
 				}
 				// Reload the chart with the updated Chart.lock file.
 				if chartRequested, err = loader.Load(cp); err != nil {
-					return nil, errors.Wrap(err, "failed reloading chart after repo update")
+					return nil, fmt.Errorf("failed reloading chart after repo update: %w", err)
 				}
 			} else {
-				return nil, err
+				return nil, fmt.Errorf("an error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies: %w", err)
 			}
 		}
 	}
@@ -333,7 +332,7 @@ func checkIfInstallable(ch *chart.Chart) error {
 	case "", "application":
 		return nil
 	}
-	return errors.Errorf("%s charts are not installable", ch.Metadata.Type)
+	return fmt.Errorf("%s charts are not installable", ch.Metadata.Type)
 }
 
 // Provide dynamic auto-completion for the install and template commands
@@ -359,7 +358,7 @@ func validateDryRunOptionFlag(dryRunOptionFlagValue string) error {
 		}
 	}
 	if !isAllowed {
-		return errors.New("Invalid dry-run flag. Flag must one of the following: false, true, none, client, server")
+		return errors.New("invalid dry-run flag. Flag must one of the following: false, true, none, client, server")
 	}
 	return nil
 }
diff --git a/pkg/cmd/lint.go b/pkg/cmd/lint.go
index 18a43a1ef..78083a7ea 100644
--- a/pkg/cmd/lint.go
+++ b/pkg/cmd/lint.go
@@ -17,13 +17,13 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"io"
 	"os"
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/action"
diff --git a/pkg/cmd/load_plugins.go b/pkg/cmd/load_plugins.go
index 3cf701242..2eef1fb3c 100644
--- a/pkg/cmd/load_plugins.go
+++ b/pkg/cmd/load_plugins.go
@@ -27,7 +27,6 @@ import (
 	"strings"
 	"syscall"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 	"sigs.k8s.io/yaml"
 
@@ -50,7 +49,6 @@ type PluginError struct {
 // to inspect its environment and then add commands to the base command
 // as it finds them.
 func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
-
 	// If HELM_NO_PLUGINS is set to 1, do not load plugins.
 	if os.Getenv("HELM_NO_PLUGINS") == "1" {
 		return
@@ -87,7 +85,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
 				main, argv, prepCmdErr := plug.PrepareCommand(u)
 				if prepCmdErr != nil {
 					os.Stderr.WriteString(prepCmdErr.Error())
-					return errors.Errorf("plugin %q exited with error", md.Name)
+					return fmt.Errorf("plugin %q exited with error", md.Name)
 				}
 
 				return callPluginExecutable(md.Name, main, argv, out)
@@ -139,7 +137,7 @@ func callPluginExecutable(pluginName string, main string, argv []string, out io.
 			os.Stderr.Write(eerr.Stderr)
 			status := eerr.Sys().(syscall.WaitStatus)
 			return PluginError{
-				error: errors.Errorf("plugin %q exited with error", pluginName),
+				error: fmt.Errorf("plugin %q exited with error", pluginName),
 				Code:  status.ExitStatus(),
 			}
 		}
diff --git a/pkg/cmd/package.go b/pkg/cmd/package.go
index 7bc22dfb4..40c503222 100644
--- a/pkg/cmd/package.go
+++ b/pkg/cmd/package.go
@@ -17,12 +17,12 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"io"
 	"os"
 	"path/filepath"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/action"
@@ -57,7 +57,7 @@ func newPackageCmd(out io.Writer) *cobra.Command {
 		Long:  packageDesc,
 		RunE: func(_ *cobra.Command, args []string) error {
 			if len(args) == 0 {
-				return errors.Errorf("need at least one argument, the path to the chart")
+				return fmt.Errorf("need at least one argument, the path to the chart")
 			}
 			if client.Sign {
 				if client.Key == "" {
diff --git a/pkg/cmd/plugin.go b/pkg/cmd/plugin.go
index 355ed4349..a2bb838df 100644
--- a/pkg/cmd/plugin.go
+++ b/pkg/cmd/plugin.go
@@ -16,12 +16,12 @@ limitations under the License.
 package cmd
 
 import (
+	"fmt"
 	"io"
 	"log/slog"
 	"os"
 	"os/exec"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/plugin"
@@ -73,7 +73,7 @@ func runHook(p *plugin.Plugin, event string) error {
 	if err := prog.Run(); err != nil {
 		if eerr, ok := err.(*exec.ExitError); ok {
 			os.Stderr.Write(eerr.Stderr)
-			return errors.Errorf("plugin %s hook for %q exited with error", event, p.Metadata.Name)
+			return fmt.Errorf("plugin %s hook for %q exited with error", event, p.Metadata.Name)
 		}
 		return err
 	}
diff --git a/pkg/cmd/plugin_install.go b/pkg/cmd/plugin_install.go
index 14469f5b4..945bf8ee0 100644
--- a/pkg/cmd/plugin_install.go
+++ b/pkg/cmd/plugin_install.go
@@ -20,7 +20,6 @@ import (
 	"io"
 	"log/slog"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/cmd/require"
@@ -83,7 +82,7 @@ func (o *pluginInstallOptions) run(out io.Writer) error {
 	slog.Debug("loading plugin", "path", i.Path())
 	p, err := plugin.LoadDir(i.Path())
 	if err != nil {
-		return errors.Wrap(err, "plugin is installed but unusable")
+		return fmt.Errorf("plugin is installed but unusable: %w", err)
 	}
 
 	if err := runHook(p, plugin.Install); err != nil {
diff --git a/pkg/cmd/plugin_uninstall.go b/pkg/cmd/plugin_uninstall.go
index 61bc3d724..ec73ad6df 100644
--- a/pkg/cmd/plugin_uninstall.go
+++ b/pkg/cmd/plugin_uninstall.go
@@ -16,13 +16,12 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"io"
 	"log/slog"
 	"os"
-	"strings"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/plugin"
@@ -66,20 +65,20 @@ func (o *pluginUninstallOptions) run(out io.Writer) error {
 	if err != nil {
 		return err
 	}
-	var errorPlugins []string
+	var errorPlugins []error
 	for _, name := range o.names {
 		if found := findPlugin(plugins, name); found != nil {
 			if err := uninstallPlugin(found); err != nil {
-				errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to uninstall plugin %s, got error (%v)", name, err))
+				errorPlugins = append(errorPlugins, fmt.Errorf("failed to uninstall plugin %s, got error (%v)", name, err))
 			} else {
 				fmt.Fprintf(out, "Uninstalled plugin: %s\n", name)
 			}
 		} else {
-			errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name))
+			errorPlugins = append(errorPlugins, fmt.Errorf("plugin: %s not found", name))
 		}
 	}
 	if len(errorPlugins) > 0 {
-		return errors.New(strings.Join(errorPlugins, "\n"))
+		return errors.Join(errorPlugins...)
 	}
 	return nil
 }
diff --git a/pkg/cmd/plugin_update.go b/pkg/cmd/plugin_update.go
index c9a8ca238..59d884877 100644
--- a/pkg/cmd/plugin_update.go
+++ b/pkg/cmd/plugin_update.go
@@ -16,13 +16,12 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"io"
 	"log/slog"
 	"path/filepath"
-	"strings"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/plugin"
@@ -68,21 +67,21 @@ func (o *pluginUpdateOptions) run(out io.Writer) error {
 	if err != nil {
 		return err
 	}
-	var errorPlugins []string
+	var errorPlugins []error
 
 	for _, name := range o.names {
 		if found := findPlugin(plugins, name); found != nil {
 			if err := updatePlugin(found); err != nil {
-				errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to update plugin %s, got error (%v)", name, err))
+				errorPlugins = append(errorPlugins, fmt.Errorf("failed to update plugin %s, got error (%v)", name, err))
 			} else {
 				fmt.Fprintf(out, "Updated plugin: %s\n", name)
 			}
 		} else {
-			errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name))
+			errorPlugins = append(errorPlugins, fmt.Errorf("plugin: %s not found", name))
 		}
 	}
 	if len(errorPlugins) > 0 {
-		return errors.New(strings.Join(errorPlugins, "\n"))
+		return errors.Join(errorPlugins...)
 	}
 	return nil
 }
diff --git a/pkg/cmd/repo.go b/pkg/cmd/repo.go
index 925669e13..0dc2a7175 100644
--- a/pkg/cmd/repo.go
+++ b/pkg/cmd/repo.go
@@ -17,10 +17,10 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"io"
-	"os"
+	"io/fs"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/cmd/require"
@@ -50,5 +50,5 @@ func newRepoCmd(out io.Writer) *cobra.Command {
 }
 
 func isNotExist(err error) bool {
-	return os.IsNotExist(errors.Cause(err))
+	return errors.Is(err, fs.ErrNotExist)
 }
diff --git a/pkg/cmd/repo_add.go b/pkg/cmd/repo_add.go
index f6c0c11c0..24c1eecab 100644
--- a/pkg/cmd/repo_add.go
+++ b/pkg/cmd/repo_add.go
@@ -18,15 +18,16 @@ package cmd
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"strings"
 	"time"
 
 	"github.com/gofrs/flock"
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 	"golang.org/x/term"
 	"sigs.k8s.io/yaml"
@@ -135,7 +136,7 @@ func (o *repoAddOptions) run(out io.Writer) error {
 	}
 
 	b, err := os.ReadFile(o.repoFile)
-	if err != nil && !os.IsNotExist(err) {
+	if err != nil && !errors.Is(err, fs.ErrNotExist) {
 		return err
 	}
 
@@ -179,7 +180,7 @@ func (o *repoAddOptions) run(out io.Writer) error {
 
 	// Check if the repo name is legal
 	if strings.Contains(o.name, "/") {
-		return errors.Errorf("repository name (%s) contains '/', please specify a different name without '/'", o.name)
+		return fmt.Errorf("repository name (%s) contains '/', please specify a different name without '/'", o.name)
 	}
 
 	// If the repo exists do one of two things:
@@ -188,10 +189,9 @@ func (o *repoAddOptions) run(out io.Writer) error {
 	if !o.forceUpdate && f.Has(o.name) {
 		existing := f.Get(o.name)
 		if c != *existing {
-
 			// The input coming in for the name is different from what is already
 			// configured. Return an error.
-			return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name)
+			return fmt.Errorf("repository name (%s) already exists, please specify a different name", o.name)
 		}
 
 		// The add is idempotent so do nothing
@@ -208,12 +208,12 @@ func (o *repoAddOptions) run(out io.Writer) error {
 		r.CachePath = o.repoCache
 	}
 	if _, err := r.DownloadIndexFile(); err != nil {
-		return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", o.url)
+		return fmt.Errorf("looks like %q is not a valid chart repository or cannot be reached: %w", o.url, err)
 	}
 
 	f.Update(&c)
 
-	if err := f.WriteFile(o.repoFile, 0600); err != nil {
+	if err := f.WriteFile(o.repoFile, 0o600); err != nil {
 		return err
 	}
 	fmt.Fprintf(out, "%q has been added to your repositories\n", o.name)
diff --git a/pkg/cmd/repo_index.go b/pkg/cmd/repo_index.go
index 13a0a9439..c17fd9391 100644
--- a/pkg/cmd/repo_index.go
+++ b/pkg/cmd/repo_index.go
@@ -17,11 +17,13 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
+	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path/filepath"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/cmd/require"
@@ -97,13 +99,13 @@ func index(dir, url, mergeTo string, json bool) error {
 	if mergeTo != "" {
 		// if index.yaml is missing then create an empty one to merge into
 		var i2 *repo.IndexFile
-		if _, err := os.Stat(mergeTo); os.IsNotExist(err) {
+		if _, err := os.Stat(mergeTo); errors.Is(err, fs.ErrNotExist) {
 			i2 = repo.NewIndexFile()
 			writeIndexFile(i2, mergeTo, json)
 		} else {
 			i2, err = repo.LoadIndexFile(mergeTo)
 			if err != nil {
-				return errors.Wrap(err, "merge failed")
+				return fmt.Errorf("merge failed: %w", err)
 			}
 		}
 		i.Merge(i2)
@@ -114,7 +116,7 @@ func index(dir, url, mergeTo string, json bool) error {
 
 func writeIndexFile(i *repo.IndexFile, out string, json bool) error {
 	if json {
-		return i.WriteJSONFile(out, 0644)
+		return i.WriteJSONFile(out, 0o644)
 	}
-	return i.WriteFile(out, 0644)
+	return i.WriteFile(out, 0o644)
 }
diff --git a/pkg/cmd/repo_list.go b/pkg/cmd/repo_list.go
index 71324dc85..60c879984 100644
--- a/pkg/cmd/repo_list.go
+++ b/pkg/cmd/repo_list.go
@@ -17,11 +17,11 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"io"
 
 	"github.com/gosuri/uitable"
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/cli/output"
diff --git a/pkg/cmd/repo_remove.go b/pkg/cmd/repo_remove.go
index 97630810a..d0a3aa205 100644
--- a/pkg/cmd/repo_remove.go
+++ b/pkg/cmd/repo_remove.go
@@ -17,12 +17,13 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path/filepath"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/cmd/require"
@@ -65,7 +66,7 @@ func (o *repoRemoveOptions) run(out io.Writer) error {
 
 	for _, name := range o.names {
 		if !r.Remove(name) {
-			return errors.Errorf("no repo named %q found", name)
+			return fmt.Errorf("no repo named %q found", name)
 		}
 		if err := r.WriteFile(o.repoFile, 0600); err != nil {
 			return err
@@ -87,10 +88,10 @@ func removeRepoCache(root, name string) error {
 	}
 
 	idx = filepath.Join(root, helmpath.CacheIndexFile(name))
-	if _, err := os.Stat(idx); os.IsNotExist(err) {
+	if _, err := os.Stat(idx); errors.Is(err, fs.ErrNotExist) {
 		return nil
 	} else if err != nil {
-		return errors.Wrapf(err, "can't remove index file %s", idx)
+		return fmt.Errorf("can't remove index file %s: %w", idx, err)
 	}
 	return os.Remove(idx)
 }
diff --git a/pkg/cmd/repo_update.go b/pkg/cmd/repo_update.go
index a905406cd..9f4a603ae 100644
--- a/pkg/cmd/repo_update.go
+++ b/pkg/cmd/repo_update.go
@@ -17,12 +17,12 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"io"
 	"slices"
 	"sync"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/cmd/require"
@@ -77,7 +77,7 @@ func (o *repoUpdateOptions) run(out io.Writer) error {
 	case isNotExist(err):
 		return errNoRepositories
 	case err != nil:
-		return errors.Wrapf(err, "failed loading file: %s", o.repoFile)
+		return fmt.Errorf("failed loading file: %s: %w", o.repoFile, err)
 	case len(f.Repositories) == 0:
 		return errNoRepositories
 	}
@@ -155,7 +155,7 @@ func checkRequestedRepos(requestedRepos []string, validRepos []*repo.Entry) erro
 			}
 		}
 		if !found {
-			return errors.Errorf("no repositories found matching '%s'.  Nothing will be updated", requestedRepo)
+			return fmt.Errorf("no repositories found matching '%s'.  Nothing will be updated", requestedRepo)
 		}
 	}
 	return nil
diff --git a/pkg/cmd/require/args.go b/pkg/cmd/require/args.go
index cfa8a0169..f5e0888f1 100644
--- a/pkg/cmd/require/args.go
+++ b/pkg/cmd/require/args.go
@@ -16,14 +16,15 @@ limitations under the License.
 package require
 
 import (
-	"github.com/pkg/errors"
+	"fmt"
+
 	"github.com/spf13/cobra"
 )
 
 // NoArgs returns an error if any args are included.
 func NoArgs(cmd *cobra.Command, args []string) error {
 	if len(args) > 0 {
-		return errors.Errorf(
+		return fmt.Errorf(
 			"%q accepts no arguments\n\nUsage:  %s",
 			cmd.CommandPath(),
 			cmd.UseLine(),
@@ -36,7 +37,7 @@ func NoArgs(cmd *cobra.Command, args []string) error {
 func ExactArgs(n int) cobra.PositionalArgs {
 	return func(cmd *cobra.Command, args []string) error {
 		if len(args) != n {
-			return errors.Errorf(
+			return fmt.Errorf(
 				"%q requires %d %s\n\nUsage:  %s",
 				cmd.CommandPath(),
 				n,
@@ -52,7 +53,7 @@ func ExactArgs(n int) cobra.PositionalArgs {
 func MaximumNArgs(n int) cobra.PositionalArgs {
 	return func(cmd *cobra.Command, args []string) error {
 		if len(args) > n {
-			return errors.Errorf(
+			return fmt.Errorf(
 				"%q accepts at most %d %s\n\nUsage:  %s",
 				cmd.CommandPath(),
 				n,
@@ -68,7 +69,7 @@ func MaximumNArgs(n int) cobra.PositionalArgs {
 func MinimumNArgs(n int) cobra.PositionalArgs {
 	return func(cmd *cobra.Command, args []string) error {
 		if len(args) < n {
-			return errors.Errorf(
+			return fmt.Errorf(
 				"%q requires at least %d %s\n\nUsage:  %s",
 				cmd.CommandPath(),
 				n,
diff --git a/pkg/cmd/search_hub.go b/pkg/cmd/search_hub.go
index 380d1e394..cfeeec59b 100644
--- a/pkg/cmd/search_hub.go
+++ b/pkg/cmd/search_hub.go
@@ -23,7 +23,6 @@ import (
 	"strings"
 
 	"github.com/gosuri/uitable"
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/internal/monocular"
@@ -84,7 +83,7 @@ func newSearchHubCmd(out io.Writer) *cobra.Command {
 func (o *searchHubOptions) run(out io.Writer, args []string) error {
 	c, err := monocular.New(o.searchEndpoint)
 	if err != nil {
-		return errors.Wrap(err, fmt.Sprintf("unable to create connection to %q", o.searchEndpoint))
+		return fmt.Errorf("unable to create connection to %q: %w", o.searchEndpoint, err)
 	}
 
 	q := strings.Join(args, " ")
diff --git a/pkg/cmd/search_repo.go b/pkg/cmd/search_repo.go
index b93b871b1..dffa0d1c4 100644
--- a/pkg/cmd/search_repo.go
+++ b/pkg/cmd/search_repo.go
@@ -19,6 +19,7 @@ package cmd
 import (
 	"bufio"
 	"bytes"
+	"errors"
 	"fmt"
 	"io"
 	"log/slog"
@@ -28,7 +29,6 @@ import (
 
 	"github.com/Masterminds/semver/v3"
 	"github.com/gosuri/uitable"
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/cli/output"
@@ -153,7 +153,7 @@ func (o *searchRepoOptions) applyConstraint(res []*search.Result) ([]*search.Res
 
 	constraint, err := semver.NewConstraint(o.version)
 	if err != nil {
-		return res, errors.Wrap(err, "an invalid version/constraint format")
+		return res, fmt.Errorf("an invalid version/constraint format: %w", err)
 	}
 
 	data := res[:0]
diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go
index f96b25e30..7a565ef85 100644
--- a/pkg/cmd/template.go
+++ b/pkg/cmd/template.go
@@ -18,8 +18,10 @@ package cmd
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path"
 	"path/filepath"
@@ -250,7 +252,7 @@ func createOrOpenFile(filename string, appendData bool) (*os.File, error) {
 func ensureDirectoryForFile(file string) error {
 	baseDir := path.Dir(file)
 	_, err := os.Stat(baseDir)
-	if err != nil && !os.IsNotExist(err) {
+	if err != nil && !errors.Is(err, fs.ErrNotExist) {
 		return err
 	}
 
diff --git a/pkg/cmd/upgrade.go b/pkg/cmd/upgrade.go
index d1f2ad8e3..b93fa6e64 100644
--- a/pkg/cmd/upgrade.go
+++ b/pkg/cmd/upgrade.go
@@ -27,7 +27,6 @@ import (
 	"syscall"
 	"time"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v4/pkg/action"
@@ -200,7 +199,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 			}
 			if req := ch.Metadata.Dependencies; req != nil {
 				if err := action.CheckDependencies(ch, req); err != nil {
-					err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
+					err = fmt.Errorf("an error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies: %w", err)
 					if client.DependencyUpdate {
 						man := &downloader.Manager{
 							Out:              out,
@@ -217,7 +216,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 						}
 						// Reload the chart with the updated Chart.lock file.
 						if ch, err = loader.Load(chartPath); err != nil {
-							return errors.Wrap(err, "failed reloading chart after repo update")
+							return fmt.Errorf("failed reloading chart after repo update: %w", err)
 						}
 					} else {
 						return err
@@ -246,7 +245,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 
 			rel, err := client.RunWithContext(ctx, args[0], ch, vals)
 			if err != nil {
-				return errors.Wrap(err, "UPGRADE FAILED")
+				return fmt.Errorf("UPGRADE FAILED: %w", err)
 			}
 
 			if outfmt == output.Table {
diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go
index f5d1deac9..04c56e614 100644
--- a/pkg/downloader/chart_downloader.go
+++ b/pkg/downloader/chart_downloader.go
@@ -16,15 +16,15 @@ limitations under the License.
 package downloader
 
 import (
+	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"net/url"
 	"os"
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/internal/fileutil"
 	"helm.sh/helm/v4/internal/urlutil"
 	"helm.sh/helm/v4/pkg/getter"
@@ -120,7 +120,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
 		body, err := g.Get(u.String() + ".prov")
 		if err != nil {
 			if c.Verify == VerifyAlways {
-				return destfile, ver, errors.Errorf("failed to fetch provenance %q", u.String()+".prov")
+				return destfile, ver, fmt.Errorf("failed to fetch provenance %q", u.String()+".prov")
 			}
 			fmt.Fprintf(c.Out, "WARNING: Verification not found for %s: %s\n", ref, err)
 			return destfile, ver, nil
@@ -160,7 +160,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
 func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, error) {
 	u, err := url.Parse(ref)
 	if err != nil {
-		return nil, errors.Errorf("invalid chart URL format: %s", ref)
+		return nil, fmt.Errorf("invalid chart URL format: %s", ref)
 	}
 
 	if registry.IsOCI(u.String()) {
@@ -213,13 +213,12 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
 	// See if it's of the form: repo/path_to_chart
 	p := strings.SplitN(u.Path, "/", 2)
 	if len(p) < 2 {
-		return u, errors.Errorf("non-absolute URLs should be in form of repo_name/path_to_chart, got: %s", u)
+		return u, fmt.Errorf("non-absolute URLs should be in form of repo_name/path_to_chart, got: %s", u)
 	}
 
 	repoName := p[0]
 	chartName := p[1]
 	rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories)
-
 	if err != nil {
 		return u, err
 	}
@@ -249,23 +248,22 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
 	idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
 	i, err := repo.LoadIndexFile(idxFile)
 	if err != nil {
-		return u, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
+		return u, fmt.Errorf("no cached repo found. (try 'helm repo update'): %w", err)
 	}
 
 	cv, err := i.Get(chartName, version)
 	if err != nil {
-		return u, errors.Wrapf(err, "chart %q matching %s not found in %s index. (try 'helm repo update')", chartName, version, r.Config.Name)
+		return u, fmt.Errorf("chart %q matching %s not found in %s index. (try 'helm repo update'): %w", chartName, version, r.Config.Name, err)
 	}
 
 	if len(cv.URLs) == 0 {
-		return u, errors.Errorf("chart %q has no downloadable URLs", ref)
+		return u, fmt.Errorf("chart %q has no downloadable URLs", ref)
 	}
 
 	// TODO: Seems that picking first URL is not fully correct
 	resolvedURL, err := repo.ResolveReferenceURL(rc.URL, cv.URLs[0])
-
 	if err != nil {
-		return u, errors.Errorf("invalid chart URL format: %s", ref)
+		return u, fmt.Errorf("invalid chart URL format: %s", ref)
 	}
 
 	return url.Parse(resolvedURL)
@@ -288,12 +286,12 @@ func VerifyChart(path, keyring string) (*provenance.Verification, error) {
 
 	provfile := path + ".prov"
 	if _, err := os.Stat(provfile); err != nil {
-		return nil, errors.Wrapf(err, "could not load provenance file %s", provfile)
+		return nil, fmt.Errorf("could not load provenance file %s: %w", provfile, err)
 	}
 
 	sig, err := provenance.NewFromKeyring(keyring, "")
 	if err != nil {
-		return nil, errors.Wrap(err, "failed to load keyring")
+		return nil, fmt.Errorf("failed to load keyring: %w", err)
 	}
 	return sig.Verify(path, provfile)
 }
@@ -310,12 +308,12 @@ func pickChartRepositoryConfigByName(name string, cfgs []*repo.Entry) (*repo.Ent
 	for _, rc := range cfgs {
 		if rc.Name == name {
 			if rc.URL == "" {
-				return nil, errors.Errorf("no URL found for repository %s", name)
+				return nil, fmt.Errorf("no URL found for repository %s", name)
 			}
 			return rc, nil
 		}
 	}
-	return nil, errors.Errorf("repo %s not found", name)
+	return nil, fmt.Errorf("repo %s not found", name)
 }
 
 // scanReposForURL scans all repos to find which repo contains the given URL.
@@ -348,7 +346,7 @@ func (c *ChartDownloader) scanReposForURL(u string, rf *repo.File) (*repo.Entry,
 		idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
 		i, err := repo.LoadIndexFile(idxFile)
 		if err != nil {
-			return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
+			return nil, fmt.Errorf("no cached repo found. (try 'helm repo update'): %w", err)
 		}
 
 		for _, entry := range i.Entries {
@@ -367,7 +365,7 @@ func (c *ChartDownloader) scanReposForURL(u string, rf *repo.File) (*repo.Entry,
 
 func loadRepoConfig(file string) (*repo.File, error) {
 	r, err := repo.LoadFile(file)
-	if err != nil && !os.IsNotExist(errors.Cause(err)) {
+	if err != nil && !errors.Is(err, fs.ErrNotExist) {
 		return nil, err
 	}
 	return r, nil
diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go
index 84eff633d..e884e12d4 100644
--- a/pkg/downloader/manager.go
+++ b/pkg/downloader/manager.go
@@ -18,8 +18,10 @@ package downloader
 import (
 	"crypto"
 	"encoding/hex"
+	"errors"
 	"fmt"
 	"io"
+	stdfs "io/fs"
 	"log"
 	"net/url"
 	"os"
@@ -30,7 +32,6 @@ import (
 	"sync"
 
 	"github.com/Masterminds/semver/v3"
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	"helm.sh/helm/v4/internal/resolver"
@@ -220,7 +221,7 @@ func (m *Manager) Update() error {
 
 func (m *Manager) loadChartDir() (*chart.Chart, error) {
 	if fi, err := os.Stat(m.ChartPath); err != nil {
-		return nil, errors.Wrapf(err, "could not find %s", m.ChartPath)
+		return nil, fmt.Errorf("could not find %s: %w", m.ChartPath, err)
 	} else if !fi.IsDir() {
 		return nil, errors.New("only unpacked charts can be updated")
 	}
@@ -251,9 +252,9 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
 	// Check if 'charts' directory is not actually a directory. If it does not exist, create it.
 	if fi, err := os.Stat(destPath); err == nil {
 		if !fi.IsDir() {
-			return errors.Errorf("%q is not a directory", destPath)
+			return fmt.Errorf("%q is not a directory", destPath)
 		}
-	} else if os.IsNotExist(err) {
+	} else if errors.Is(err, stdfs.ErrNotExist) {
 		if err := os.MkdirAll(destPath, 0755); err != nil {
 			return err
 		}
@@ -314,7 +315,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
 		// https://github.com/helm/helm/issues/1439
 		churl, username, password, insecureskiptlsverify, passcredentialsall, caFile, certFile, keyFile, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos)
 		if err != nil {
-			saveError = errors.Wrapf(err, "could not find %s", churl)
+			saveError = fmt.Errorf("could not find %s: %w", churl, err)
 			break
 		}
 
@@ -345,7 +346,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
 		if registry.IsOCI(churl) {
 			churl, version, err = parseOCIRef(churl)
 			if err != nil {
-				return errors.Wrapf(err, "could not parse OCI reference")
+				return fmt.Errorf("could not parse OCI reference: %w", err)
 			}
 			dl.Options = append(dl.Options,
 				getter.WithRegistryClient(m.RegistryClient),
@@ -353,7 +354,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
 		}
 
 		if _, _, err = dl.DownloadTo(churl, version, tmpPath); err != nil {
-			saveError = errors.Wrapf(err, "could not download %s", churl)
+			saveError = fmt.Errorf("could not download %s: %w", churl, err)
 			break
 		}
 
@@ -377,7 +378,7 @@ func parseOCIRef(chartRef string) (string, string, error) {
 	refTagRegexp := regexp.MustCompile(`^(oci://[^:]+(:[0-9]{1,5})?[^:]+):(.*)$`)
 	caps := refTagRegexp.FindStringSubmatch(chartRef)
 	if len(caps) != 4 {
-		return "", "", errors.Errorf("improperly formatted oci chart reference: %s", chartRef)
+		return "", "", fmt.Errorf("improperly formatted oci chart reference: %s", chartRef)
 	}
 	chartRef = caps[1]
 	tag := caps[3]
@@ -559,7 +560,7 @@ func (m *Manager) ensureMissingRepos(repoNames map[string]string, deps []*chart.
 func (m *Manager) resolveRepoNames(deps []*chart.Dependency) (map[string]string, error) {
 	rf, err := loadRepoConfig(m.RepositoryConfig)
 	if err != nil {
-		if os.IsNotExist(err) {
+		if errors.Is(err, stdfs.ErrNotExist) {
 			return make(map[string]string), nil
 		}
 		return nil, err
@@ -764,7 +765,7 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*
 	if err == nil {
 		return url, username, password, false, false, "", "", "", err
 	}
-	err = errors.Errorf("chart %s not found in %s: %s", name, repoURL, err)
+	err = fmt.Errorf("chart %s not found in %s: %w", name, repoURL, err)
 	return url, username, password, false, false, "", "", "", err
 }
 
@@ -820,7 +821,7 @@ func normalizeURL(baseURL, urlOrPath string) (string, error) {
 	}
 	u2, err := url.Parse(baseURL)
 	if err != nil {
-		return urlOrPath, errors.Wrap(err, "base URL failed to parse")
+		return urlOrPath, fmt.Errorf("base URL failed to parse: %w", err)
 	}
 
 	u2.RawPath = path.Join(u2.RawPath, urlOrPath)
@@ -838,7 +839,7 @@ func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, err
 	// Load repositories.yaml file
 	rf, err := loadRepoConfig(m.RepositoryConfig)
 	if err != nil {
-		return indices, errors.Wrapf(err, "failed to load %s", m.RepositoryConfig)
+		return indices, fmt.Errorf("failed to load %s: %w", m.RepositoryConfig, err)
 	}
 
 	for _, re := range rf.Repositories {
@@ -876,7 +877,7 @@ func writeLock(chartpath string, lock *chart.Lock, legacyLockfile bool) error {
 // archive a dep chart from local directory and save it into destPath
 func tarFromLocalDir(chartpath, name, repo, version, destPath string) (string, error) {
 	if !strings.HasPrefix(repo, "file://") {
-		return "", errors.Errorf("wrong format: chart %s repository %s", name, repo)
+		return "", fmt.Errorf("wrong format: chart %s repository %s", name, repo)
 	}
 
 	origPath, err := resolver.GetLocalPath(repo, chartpath)
@@ -891,7 +892,7 @@ func tarFromLocalDir(chartpath, name, repo, version, destPath string) (string, e
 
 	constraint, err := semver.NewConstraint(version)
 	if err != nil {
-		return "", errors.Wrapf(err, "dependency %s has an invalid version/constraint format", name)
+		return "", fmt.Errorf("dependency %s has an invalid version/constraint format: %w", name, err)
 	}
 
 	v, err := semver.NewVersion(ch.Metadata.Version)
@@ -904,7 +905,7 @@ func tarFromLocalDir(chartpath, name, repo, version, destPath string) (string, e
 		return ch.Metadata.Version, err
 	}
 
-	return "", errors.Errorf("can't get a valid version for dependency %s", name)
+	return "", fmt.Errorf("can't get a valid version for dependency %s", name)
 }
 
 // The prefix to use for cache keys created by the manager for repo names
diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go
index 0b0933def..839ad4a31 100644
--- a/pkg/engine/engine.go
+++ b/pkg/engine/engine.go
@@ -17,6 +17,7 @@ limitations under the License.
 package engine
 
 import (
+	"errors"
 	"fmt"
 	"log/slog"
 	"path"
@@ -26,7 +27,6 @@ import (
 	"strings"
 	"text/template"
 
-	"github.com/pkg/errors"
 	"k8s.io/client-go/rest"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
@@ -133,7 +133,9 @@ func includeFun(t *template.Template, includedNames map[string]int) func(string,
 		var buf strings.Builder
 		if v, ok := includedNames[name]; ok {
 			if v > recursionMaxNums {
-				return "", errors.Wrapf(fmt.Errorf("unable to execute template"), "rendering template has a nested reference name: %s", name)
+				return "", fmt.Errorf(
+					"rendering template has a nested reference name: %s: %w",
+					name, errors.New("unable to execute template"))
 			}
 			includedNames[name]++
 		} else {
@@ -151,7 +153,7 @@ func tplFun(parent *template.Template, includedNames map[string]int, strict bool
 	return func(tpl string, vals interface{}) (string, error) {
 		t, err := parent.Clone()
 		if err != nil {
-			return "", errors.Wrapf(err, "cannot clone template")
+			return "", fmt.Errorf("cannot clone template: %w", err)
 		}
 
 		// Re-inject the missingkey option, see text/template issue https://github.com/golang/go/issues/43022
@@ -178,12 +180,12 @@ func tplFun(parent *template.Template, includedNames map[string]int, strict bool
 		// text string. (Maybe we could use a hash appended to the name?)
 		t, err = t.New(parent.Name()).Parse(tpl)
 		if err != nil {
-			return "", errors.Wrapf(err, "cannot parse template %q", tpl)
+			return "", fmt.Errorf("cannot parse template %q: %w", tpl, err)
 		}
 
 		var buf strings.Builder
 		if err := t.Execute(&buf, vals); err != nil {
-			return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl)
+			return "", fmt.Errorf("error during tpl function execution for %q: %w", tpl, err)
 		}
 
 		// See comment in renderWithReferences explaining the <no value> hack.
@@ -265,7 +267,7 @@ func (e Engine) render(tpls map[string]renderable) (rendered map[string]string,
 	// template engine.
 	defer func() {
 		if r := recover(); r != nil {
-			err = errors.Errorf("rendering template failed: %v", r)
+			err = fmt.Errorf("rendering template failed: %v", r)
 		}
 	}()
 	t := template.New("gotpl")
diff --git a/pkg/engine/lookup_func.go b/pkg/engine/lookup_func.go
index 6c415bfb5..605b43a48 100644
--- a/pkg/engine/lookup_func.go
+++ b/pkg/engine/lookup_func.go
@@ -18,10 +18,10 @@ package engine
 
 import (
 	"context"
+	"fmt"
 	"log/slog"
 	"strings"
 
-	"github.com/pkg/errors"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
@@ -99,7 +99,7 @@ func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config)
 	apiRes, err := getAPIResourceForGVK(gvk, config)
 	if err != nil {
 		slog.Error("unable to get apiresource", "groupVersionKind", gvk.String(), slog.Any("error", err))
-		return nil, false, errors.Wrapf(err, "unable to get apiresource from unstructured: %s", gvk.String())
+		return nil, false, fmt.Errorf("unable to get apiresource from unstructured: %s: %w", gvk.String(), err)
 	}
 	gvr := schema.GroupVersionResource{
 		Group:    apiRes.Group,
diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go
index 5014784bc..743ac569b 100644
--- a/pkg/getter/getter.go
+++ b/pkg/getter/getter.go
@@ -18,11 +18,10 @@ package getter
 
 import (
 	"bytes"
+	"fmt"
 	"net/http"
 	"time"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/pkg/cli"
 	"helm.sh/helm/v4/pkg/registry"
 )
@@ -184,7 +183,7 @@ func (p Providers) ByScheme(scheme string) (Getter, error) {
 			return pp.New()
 		}
 	}
-	return nil, errors.Errorf("scheme %q not supported", scheme)
+	return nil, fmt.Errorf("scheme %q not supported", scheme)
 }
 
 const (
diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go
index a945dec2b..925df201e 100644
--- a/pkg/getter/httpgetter.go
+++ b/pkg/getter/httpgetter.go
@@ -18,13 +18,12 @@ package getter
 import (
 	"bytes"
 	"crypto/tls"
+	"fmt"
 	"io"
 	"net/http"
 	"net/url"
 	"sync"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/internal/tlsutil"
 	"helm.sh/helm/v4/internal/version"
 )
@@ -65,11 +64,11 @@ func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) {
 	// with the basic auth is the one being fetched.
 	u1, err := url.Parse(g.opts.url)
 	if err != nil {
-		return nil, errors.Wrap(err, "Unable to parse getter URL")
+		return nil, fmt.Errorf("unable to parse getter URL: %w", err)
 	}
 	u2, err := url.Parse(href)
 	if err != nil {
-		return nil, errors.Wrap(err, "Unable to parse URL getting from")
+		return nil, fmt.Errorf("unable to parse URL getting from: %w", err)
 	}
 
 	// Host on URL (returned from url.Parse) contains the port if present.
@@ -92,7 +91,7 @@ func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) {
 	}
 	defer resp.Body.Close()
 	if resp.StatusCode != http.StatusOK {
-		return nil, errors.Errorf("failed to fetch %s : %s", href, resp.Status)
+		return nil, fmt.Errorf("failed to fetch %s : %s", href, resp.Status)
 	}
 
 	buf := bytes.NewBuffer(nil)
@@ -133,7 +132,7 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) {
 			tlsutil.WithCAFile(g.opts.caFile),
 		)
 		if err != nil {
-			return nil, errors.Wrap(err, "can't create TLS config for client")
+			return nil, fmt.Errorf("can't create TLS config for client: %w", err)
 		}
 
 		g.transport.TLSClientConfig = tlsConf
diff --git a/pkg/getter/plugingetter.go b/pkg/getter/plugingetter.go
index 2b734fdf0..3b8185543 100644
--- a/pkg/getter/plugingetter.go
+++ b/pkg/getter/plugingetter.go
@@ -23,8 +23,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/pkg/cli"
 	"helm.sh/helm/v4/pkg/plugin"
 )
@@ -86,7 +84,7 @@ func (p *pluginGetter) Get(href string, options ...Option) (*bytes.Buffer, error
 	if err := prog.Run(); err != nil {
 		if eerr, ok := err.(*exec.ExitError); ok {
 			os.Stderr.Write(eerr.Stderr)
-			return nil, errors.Errorf("plugin %q exited with error", p.command)
+			return nil, fmt.Errorf("plugin %q exited with error", p.command)
 		}
 		return nil, err
 	}
diff --git a/pkg/ignore/rules.go b/pkg/ignore/rules.go
index 02a3777ff..5281c3d59 100644
--- a/pkg/ignore/rules.go
+++ b/pkg/ignore/rules.go
@@ -19,13 +19,12 @@ package ignore
 import (
 	"bufio"
 	"bytes"
+	"errors"
 	"io"
 	"log/slog"
 	"os"
 	"path/filepath"
 	"strings"
-
-	"github.com/pkg/errors"
 )
 
 // HelmIgnore default name of an ignorefile.
diff --git a/pkg/kube/client.go b/pkg/kube/client.go
index ff0f05e88..a812fc198 100644
--- a/pkg/kube/client.go
+++ b/pkg/kube/client.go
@@ -20,6 +20,7 @@ import (
 	"bytes"
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"log/slog"
@@ -30,7 +31,6 @@ import (
 	"sync"
 
 	jsonpatch "github.com/evanphx/json-patch"
-	"github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
 	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
@@ -183,13 +183,13 @@ func (c *Client) IsReachable() error {
 	if err == genericclioptions.ErrEmptyConfig {
 		// re-replace kubernetes ErrEmptyConfig error with a friendly error
 		// moar workarounds for Kubernetes API breaking.
-		return errors.New("Kubernetes cluster unreachable")
+		return errors.New("kubernetes cluster unreachable")
 	}
 	if err != nil {
-		return errors.Wrap(err, "Kubernetes cluster unreachable")
+		return fmt.Errorf("kubernetes cluster unreachable: %w", err)
 	}
 	if _, err := client.Discovery().ServerVersion(); err != nil {
-		return errors.Wrap(err, "Kubernetes cluster unreachable")
+		return fmt.Errorf("kubernetes cluster unreachable: %w", err)
 	}
 	return nil
 }
@@ -398,7 +398,7 @@ func (c *Client) BuildTable(reader io.Reader, validate bool) (ResourceList, erro
 }
 
 func (c *Client) update(original, target ResourceList, force, threeWayMerge bool) (*Result, error) {
-	updateErrors := []string{}
+	updateErrors := []error{}
 	res := &Result{}
 
 	slog.Debug("checking resources for changes", "resources", len(target))
@@ -410,7 +410,7 @@ func (c *Client) update(original, target ResourceList, force, threeWayMerge bool
 		helper := resource.NewHelper(info.Client, info.Mapping).WithFieldManager(getManagedFieldsManager())
 		if _, err := helper.Get(info.Namespace, info.Name); err != nil {
 			if !apierrors.IsNotFound(err) {
-				return errors.Wrap(err, "could not get information about the resource")
+				return fmt.Errorf("could not get information about the resource: %w", err)
 			}
 
 			// Append the created resource to the results, even if something fails
@@ -418,7 +418,7 @@ func (c *Client) update(original, target ResourceList, force, threeWayMerge bool
 
 			// Since the resource does not exist, create it.
 			if err := createResource(info); err != nil {
-				return errors.Wrap(err, "failed to create resource")
+				return fmt.Errorf("failed to create resource: %w", err)
 			}
 
 			kind := info.Mapping.GroupVersionKind.Kind
@@ -429,12 +429,12 @@ func (c *Client) update(original, target ResourceList, force, threeWayMerge bool
 		originalInfo := original.Get(info)
 		if originalInfo == nil {
 			kind := info.Mapping.GroupVersionKind.Kind
-			return errors.Errorf("no %s with the name %q found", kind, info.Name)
+			return fmt.Errorf("no %s with the name %q found", kind, info.Name)
 		}
 
 		if err := updateResource(c, info, originalInfo.Object, force, threeWayMerge); err != nil {
 			slog.Debug("error updating the resource", "namespace", info.Namespace, "name", info.Name, "kind", info.Mapping.GroupVersionKind.Kind, slog.Any("error", err))
-			updateErrors = append(updateErrors, err.Error())
+			updateErrors = append(updateErrors, err)
 		}
 		// Because we check for errors later, append the info regardless
 		res.Updated = append(res.Updated, info)
@@ -446,7 +446,7 @@ func (c *Client) update(original, target ResourceList, force, threeWayMerge bool
 	case err != nil:
 		return res, err
 	case len(updateErrors) != 0:
-		return res, errors.New(strings.Join(updateErrors, " && "))
+		return res, joinErrors(updateErrors, " && ")
 	}
 
 	for _, info := range original.Difference(target) {
@@ -610,24 +610,24 @@ func deleteResource(info *resource.Info, policy metav1.DeletionPropagation) erro
 func createPatch(target *resource.Info, current runtime.Object, threeWayMergeForUnstructured bool) ([]byte, types.PatchType, error) {
 	oldData, err := json.Marshal(current)
 	if err != nil {
-		return nil, types.StrategicMergePatchType, errors.Wrap(err, "serializing current configuration")
+		return nil, types.StrategicMergePatchType, fmt.Errorf("serializing current configuration: %w", err)
 	}
 	newData, err := json.Marshal(target.Object)
 	if err != nil {
-		return nil, types.StrategicMergePatchType, errors.Wrap(err, "serializing target configuration")
+		return nil, types.StrategicMergePatchType, fmt.Errorf("serializing target configuration: %w", err)
 	}
 
 	// Fetch the current object for the three way merge
 	helper := resource.NewHelper(target.Client, target.Mapping).WithFieldManager(getManagedFieldsManager())
 	currentObj, err := helper.Get(target.Namespace, target.Name)
 	if err != nil && !apierrors.IsNotFound(err) {
-		return nil, types.StrategicMergePatchType, errors.Wrapf(err, "unable to get data for current object %s/%s", target.Namespace, target.Name)
+		return nil, types.StrategicMergePatchType, fmt.Errorf("unable to get data for current object %s/%s: %w", target.Namespace, target.Name, err)
 	}
 
 	// Even if currentObj is nil (because it was not found), it will marshal just fine
 	currentData, err := json.Marshal(currentObj)
 	if err != nil {
-		return nil, types.StrategicMergePatchType, errors.Wrap(err, "serializing live configuration")
+		return nil, types.StrategicMergePatchType, fmt.Errorf("serializing live configuration: %w", err)
 	}
 
 	// Get a versioned object
@@ -663,7 +663,7 @@ func createPatch(target *resource.Info, current runtime.Object, threeWayMergeFor
 
 	patchMeta, err := strategicpatch.NewPatchMetaFromStruct(versionedObject)
 	if err != nil {
-		return nil, types.StrategicMergePatchType, errors.Wrap(err, "unable to create patch metadata from object")
+		return nil, types.StrategicMergePatchType, fmt.Errorf("unable to create patch metadata from object: %w", err)
 	}
 
 	patch, err := strategicpatch.CreateThreeWayMergePatch(oldData, newData, currentData, patchMeta, true)
@@ -682,13 +682,13 @@ func updateResource(_ *Client, target *resource.Info, currentObj runtime.Object,
 		var err error
 		obj, err = helper.Replace(target.Namespace, target.Name, true, target.Object)
 		if err != nil {
-			return errors.Wrap(err, "failed to replace object")
+			return fmt.Errorf("failed to replace object: %w", err)
 		}
 		slog.Debug("replace succeeded", "name", target.Name, "initialKind", currentObj.GetObjectKind().GroupVersionKind().Kind, "kind", kind)
 	} else {
 		patch, patchType, err := createPatch(target, currentObj, threeWayMergeForUnstructured)
 		if err != nil {
-			return errors.Wrap(err, "failed to create patch")
+			return fmt.Errorf("failed to create patch: %w", err)
 		}
 
 		if patch == nil || string(patch) == "{}" {
@@ -696,7 +696,7 @@ func updateResource(_ *Client, target *resource.Info, currentObj runtime.Object,
 			// This needs to happen to make sure that Helm has the latest info from the API
 			// Otherwise there will be no labels and other functions that use labels will panic
 			if err := target.Get(); err != nil {
-				return errors.Wrap(err, "failed to refresh resource information")
+				return fmt.Errorf("failed to refresh resource information: %w", err)
 			}
 			return nil
 		}
@@ -704,7 +704,7 @@ func updateResource(_ *Client, target *resource.Info, currentObj runtime.Object,
 		slog.Debug("patching resource", "kind", kind, "name", target.Name, "namespace", target.Namespace)
 		obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil)
 		if err != nil {
-			return errors.Wrapf(err, "cannot patch %q with kind %s", target.Name, kind)
+			return fmt.Errorf("cannot patch %q with kind %s: %w", target.Name, kind, err)
 		}
 	}
 
@@ -741,12 +741,12 @@ func (c *Client) OutputContainerLogsForPodList(podList *v1.PodList, namespace st
 func copyRequestStreamToWriter(request *rest.Request, podName, containerName string, writer io.Writer) error {
 	readCloser, err := request.Stream(context.Background())
 	if err != nil {
-		return errors.Errorf("Failed to stream pod logs for pod: %s, container: %s", podName, containerName)
+		return fmt.Errorf("failed to stream pod logs for pod: %s, container: %s", podName, containerName)
 	}
 	defer readCloser.Close()
 	_, err = io.Copy(writer, readCloser)
 	if err != nil {
-		return errors.Errorf("Failed to copy IO from logs for pod: %s, container: %s", podName, containerName)
+		return fmt.Errorf("failed to copy IO from logs for pod: %s, container: %s", podName, containerName)
 	}
 	return nil
 }
@@ -763,3 +763,27 @@ func scrubValidationError(err error) error {
 	}
 	return err
 }
+
+type joinedErrors struct {
+	errs []error
+	sep  string
+}
+
+func joinErrors(errs []error, sep string) error {
+	return &joinedErrors{
+		errs: errs,
+		sep:  sep,
+	}
+}
+
+func (e *joinedErrors) Error() string {
+	errs := make([]string, 0, len(e.errs))
+	for _, err := range e.errs {
+		errs = append(errs, err.Error())
+	}
+	return strings.Join(errs, e.sep)
+}
+
+func (e *joinedErrors) Unwrap() []error {
+	return e.errs
+}
diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go
index bdd17b152..ebb5b3257 100644
--- a/pkg/kube/wait.go
+++ b/pkg/kube/wait.go
@@ -18,13 +18,12 @@ package kube // import "helm.sh/helm/v4/pkg/kube"
 
 import (
 	"context"
-	stderrors "errors"
+	"errors"
 	"fmt"
 	"log/slog"
 	"net/http"
 	"time"
 
-	"github.com/pkg/errors"
 	appsv1 "k8s.io/api/apps/v1"
 	appsv1beta1 "k8s.io/api/apps/v1beta1"
 	appsv1beta2 "k8s.io/api/apps/v1beta2"
@@ -191,7 +190,11 @@ func SelectorsForObject(object runtime.Object) (selector labels.Selector, err er
 		return nil, fmt.Errorf("selector for %T not implemented", object)
 	}
 
-	return selector, errors.Wrap(err, "invalid label selector")
+	if err != nil {
+		return selector, fmt.Errorf("invalid label selector: %w", err)
+	}
+
+	return selector, nil
 }
 
 func (hw *legacyWaiter) watchTimeout(t time.Duration) func(*resource.Info) error {
@@ -233,7 +236,7 @@ func perform(infos ResourceList, fn func(*resource.Info) error) error {
 	for range infos {
 		err := <-errs
 		if err != nil {
-			result = stderrors.Join(result, err)
+			result = errors.Join(result, err)
 		}
 	}
 
@@ -291,7 +294,7 @@ func (hw *legacyWaiter) watchUntilReady(timeout time.Duration, info *resource.In
 		case watch.Error:
 			// Handle error and return with an error.
 			slog.Error("error event received", "resource", info.Name)
-			return true, errors.Errorf("failed to deploy %s", info.Name)
+			return true, fmt.Errorf("failed to deploy %s", info.Name)
 		default:
 			return false, nil
 		}
@@ -305,7 +308,7 @@ func (hw *legacyWaiter) watchUntilReady(timeout time.Duration, info *resource.In
 func (hw *legacyWaiter) waitForJob(obj runtime.Object, name string) (bool, error) {
 	o, ok := obj.(*batchv1.Job)
 	if !ok {
-		return true, errors.Errorf("expected %s to be a *batch.Job, got %T", name, obj)
+		return true, fmt.Errorf("expected %s to be a *batch.Job, got %T", name, obj)
 	}
 
 	for _, c := range o.Status.Conditions {
@@ -313,7 +316,7 @@ func (hw *legacyWaiter) waitForJob(obj runtime.Object, name string) (bool, error
 			return true, nil
 		} else if c.Type == batchv1.JobFailed && c.Status == "True" {
 			slog.Error("job failed", "job", name, "reason", c.Reason)
-			return true, errors.Errorf("job %s failed: %s", name, c.Reason)
+			return true, fmt.Errorf("job %s failed: %s", name, c.Reason)
 		}
 	}
 
@@ -327,7 +330,7 @@ func (hw *legacyWaiter) waitForJob(obj runtime.Object, name string) (bool, error
 func (hw *legacyWaiter) waitForPodSuccess(obj runtime.Object, name string) (bool, error) {
 	o, ok := obj.(*corev1.Pod)
 	if !ok {
-		return true, errors.Errorf("expected %s to be a *v1.Pod, got %T", name, obj)
+		return true, fmt.Errorf("expected %s to be a *v1.Pod, got %T", name, obj)
 	}
 
 	switch o.Status.Phase {
@@ -336,7 +339,7 @@ func (hw *legacyWaiter) waitForPodSuccess(obj runtime.Object, name string) (bool
 		return true, nil
 	case corev1.PodFailed:
 		slog.Error("pod failed", "pod", o.Name)
-		return true, errors.Errorf("pod %s failed", o.Name)
+		return true, fmt.Errorf("pod %s failed", o.Name)
 	case corev1.PodPending:
 		slog.Debug("pod pending", "pod", o.Name)
 	case corev1.PodRunning:
diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go
index 13ae77222..724c3f2ea 100644
--- a/pkg/lint/rules/chartfile.go
+++ b/pkg/lint/rules/chartfile.go
@@ -17,13 +17,13 @@ limitations under the License.
 package rules // import "helm.sh/helm/v4/pkg/lint/rules"
 
 import (
+	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
 
 	"github.com/Masterminds/semver/v3"
 	"github.com/asaskevich/govalidator"
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	chart "helm.sh/helm/v4/pkg/chart/v2"
@@ -84,7 +84,7 @@ func isStringValue(data map[string]interface{}, key string) error {
 	}
 	valueType := fmt.Sprintf("%T", value)
 	if valueType != "string" {
-		return errors.Errorf("%s should be of type string but it's of type %s", key, valueType)
+		return fmt.Errorf("%s should be of type string but it's of type %s", key, valueType)
 	}
 	return nil
 }
@@ -100,14 +100,14 @@ func validateChartYamlNotDirectory(chartPath string) error {
 
 func validateChartYamlFormat(chartFileError error) error {
 	if chartFileError != nil {
-		return errors.Errorf("unable to parse YAML\n\t%s", chartFileError.Error())
+		return fmt.Errorf("unable to parse YAML\n\t%w", chartFileError)
 	}
 	return nil
 }
 
 func validateChartYamlStrictFormat(chartFileError error) error {
 	if chartFileError != nil {
-		return errors.Errorf("failed to strictly parse chart metadata file\n\t%s", chartFileError.Error())
+		return fmt.Errorf("failed to strictly parse chart metadata file\n\t%w", chartFileError)
 	}
 	return nil
 }
@@ -141,9 +141,8 @@ func validateChartVersion(cf *chart.Metadata) error {
 	}
 
 	version, err := semver.NewVersion(cf.Version)
-
 	if err != nil {
-		return errors.Errorf("version '%s' is not a valid SemVer", cf.Version)
+		return fmt.Errorf("version '%s' is not a valid SemVer", cf.Version)
 	}
 
 	c, err := semver.NewConstraint(">0.0.0-0")
@@ -153,7 +152,7 @@ func validateChartVersion(cf *chart.Metadata) error {
 	valid, msg := c.Validate(version)
 
 	if !valid && len(msg) > 0 {
-		return errors.Errorf("version %v", msg[0])
+		return fmt.Errorf("version %v", msg[0])
 	}
 
 	return nil
@@ -164,9 +163,9 @@ func validateChartMaintainer(cf *chart.Metadata) error {
 		if maintainer.Name == "" {
 			return errors.New("each maintainer requires a name")
 		} else if maintainer.Email != "" && !govalidator.IsEmail(maintainer.Email) {
-			return errors.Errorf("invalid email '%s' for maintainer '%s'", maintainer.Email, maintainer.Name)
+			return fmt.Errorf("invalid email '%s' for maintainer '%s'", maintainer.Email, maintainer.Name)
 		} else if maintainer.URL != "" && !govalidator.IsURL(maintainer.URL) {
-			return errors.Errorf("invalid url '%s' for maintainer '%s'", maintainer.URL, maintainer.Name)
+			return fmt.Errorf("invalid url '%s' for maintainer '%s'", maintainer.URL, maintainer.Name)
 		}
 	}
 	return nil
@@ -175,7 +174,7 @@ func validateChartMaintainer(cf *chart.Metadata) error {
 func validateChartSources(cf *chart.Metadata) error {
 	for _, source := range cf.Sources {
 		if source == "" || !govalidator.IsRequestURL(source) {
-			return errors.Errorf("invalid source URL '%s'", source)
+			return fmt.Errorf("invalid source URL '%s'", source)
 		}
 	}
 	return nil
@@ -190,7 +189,7 @@ func validateChartIconPresence(cf *chart.Metadata) error {
 
 func validateChartIconURL(cf *chart.Metadata) error {
 	if cf.Icon != "" && !govalidator.IsRequestURL(cf.Icon) {
-		return errors.Errorf("invalid icon URL '%s'", cf.Icon)
+		return fmt.Errorf("invalid icon URL '%s'", cf.Icon)
 	}
 	return nil
 }
diff --git a/pkg/lint/rules/dependencies.go b/pkg/lint/rules/dependencies.go
index 2ab56eca5..16c9d6435 100644
--- a/pkg/lint/rules/dependencies.go
+++ b/pkg/lint/rules/dependencies.go
@@ -20,8 +20,6 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	chart "helm.sh/helm/v4/pkg/chart/v2"
 	"helm.sh/helm/v4/pkg/chart/v2/loader"
 	"helm.sh/helm/v4/pkg/lint/support"
@@ -43,7 +41,7 @@ func Dependencies(linter *support.Linter) {
 
 func validateChartFormat(chartError error) error {
 	if chartError != nil {
-		return errors.Errorf("unable to load chart\n\t%s", chartError)
+		return fmt.Errorf("unable to load chart\n\t%w", chartError)
 	}
 	return nil
 }
diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go
index 81a18b411..135ebf90a 100644
--- a/pkg/lint/rules/template.go
+++ b/pkg/lint/rules/template.go
@@ -19,6 +19,7 @@ package rules
 import (
 	"bufio"
 	"bytes"
+	"errors"
 	"fmt"
 	"io"
 	"os"
@@ -26,7 +27,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/api/validation"
 	apipath "k8s.io/apimachinery/pkg/api/validation/path"
 	"k8s.io/apimachinery/pkg/util/validation/field"
@@ -212,11 +212,14 @@ func validateAllowedExtension(fileName string) error {
 		}
 	}
 
-	return errors.Errorf("file extension '%s' not valid. Valid extensions are .yaml, .yml, .tpl, or .txt", ext)
+	return fmt.Errorf("file extension '%s' not valid. Valid extensions are .yaml, .yml, .tpl, or .txt", ext)
 }
 
 func validateYamlContent(err error) error {
-	return errors.Wrap(err, "unable to parse YAML")
+	if err != nil {
+		return fmt.Errorf("unable to parse YAML: %w", err)
+	}
+	return nil
 }
 
 // validateMetadataName uses the correct validation function for the object
@@ -229,7 +232,7 @@ func validateMetadataName(obj *K8sYamlStruct) error {
 		allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("name"), obj.Metadata.Name, msg))
 	}
 	if len(allErrs) > 0 {
-		return errors.Wrapf(allErrs.ToAggregate(), "object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name)
+		return fmt.Errorf("object name does not conform to Kubernetes naming requirements: %q: %w", obj.Metadata.Name, allErrs.ToAggregate())
 	}
 	return nil
 }
@@ -293,6 +296,7 @@ func validateMatchSelector(yamlStruct *K8sYamlStruct, manifest string) error {
 	}
 	return nil
 }
+
 func validateListAnnotations(yamlStruct *K8sYamlStruct, manifest string) error {
 	if yamlStruct.Kind == "List" {
 		m := struct {
@@ -309,7 +313,7 @@ func validateListAnnotations(yamlStruct *K8sYamlStruct, manifest string) error {
 
 		for _, i := range m.Items {
 			if _, ok := i.Metadata.Annotations["helm.sh/resource-policy"]; ok {
-				return errors.New("Annotation 'helm.sh/resource-policy' within List objects are ignored")
+				return errors.New("annotation 'helm.sh/resource-policy' within List objects are ignored")
 			}
 		}
 	}
diff --git a/pkg/lint/rules/values.go b/pkg/lint/rules/values.go
index 8aae250c6..019e74fa7 100644
--- a/pkg/lint/rules/values.go
+++ b/pkg/lint/rules/values.go
@@ -17,11 +17,10 @@ limitations under the License.
 package rules
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
 
-	"github.com/pkg/errors"
-
 	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
 	"helm.sh/helm/v4/pkg/lint/support"
 )
@@ -47,7 +46,7 @@ func ValuesWithOverrides(linter *support.Linter, valueOverrides map[string]inter
 func validateValuesFileExistence(valuesPath string) error {
 	_, err := os.Stat(valuesPath)
 	if err != nil {
-		return errors.Errorf("file does not exist")
+		return fmt.Errorf("file does not exist")
 	}
 	return nil
 }
@@ -55,7 +54,7 @@ func validateValuesFileExistence(valuesPath string) error {
 func validateValuesFile(valuesPath string, overrides map[string]interface{}) error {
 	values, err := chartutil.ReadValuesFile(valuesPath)
 	if err != nil {
-		return errors.Wrap(err, "unable to parse YAML")
+		return fmt.Errorf("unable to parse YAML: %w", err)
 	}
 
 	// Helm 3.0.0 carried over the values linting from Helm 2.x, which only tests the top
diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go
index 36bbba651..7b6f28db1 100644
--- a/pkg/plugin/installer/http_installer.go
+++ b/pkg/plugin/installer/http_installer.go
@@ -19,6 +19,8 @@ import (
 	"archive/tar"
 	"bytes"
 	"compress/gzip"
+	"errors"
+	"fmt"
 	"io"
 	"log/slog"
 	"os"
@@ -28,7 +30,6 @@ import (
 	"strings"
 
 	securejoin "github.com/cyphar/filepath-securejoin"
-	"github.com/pkg/errors"
 
 	"helm.sh/helm/v4/internal/third_party/dep/fs"
 	"helm.sh/helm/v4/pkg/cli"
@@ -79,7 +80,7 @@ func NewExtractor(source string) (Extractor, error) {
 			return extractor, nil
 		}
 	}
-	return nil, errors.Errorf("no extractor implemented yet for %s", source)
+	return nil, fmt.Errorf("no extractor implemented yet for %s", source)
 }
 
 // NewHTTPInstaller creates a new HttpInstaller.
@@ -133,7 +134,7 @@ func (i *HTTPInstaller) Install() error {
 	}
 
 	if err := i.extractor.Extract(pluginData, i.CacheDir); err != nil {
-		return errors.Wrap(err, "extracting files from archive")
+		return fmt.Errorf("extracting files from archive: %w", err)
 	}
 
 	if !isPlugin(i.CacheDir) {
@@ -152,7 +153,7 @@ func (i *HTTPInstaller) Install() error {
 // Update updates a local repository
 // Not implemented for now since tarball most likely will be packaged by version
 func (i *HTTPInstaller) Update() error {
-	return errors.Errorf("method Update() not implemented for HttpInstaller")
+	return fmt.Errorf("method Update() not implemented for HttpInstaller")
 }
 
 // Path is overridden because we want to join on the plugin name not the file name
@@ -265,7 +266,7 @@ func (g *TarGzExtractor) Extract(buffer *bytes.Buffer, targetDir string) error {
 		case tar.TypeXGlobalHeader, tar.TypeXHeader:
 			continue
 		default:
-			return errors.Errorf("unknown type: %b in %s", header.Typeflag, header.Name)
+			return fmt.Errorf("unknown type: %b in %s", header.Typeflag, header.Name)
 		}
 	}
 	return nil
diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go
index 1e90bcaa0..d88737ebf 100644
--- a/pkg/plugin/installer/installer.go
+++ b/pkg/plugin/installer/installer.go
@@ -16,13 +16,12 @@ limitations under the License.
 package installer
 
 import (
+	"errors"
 	"net/http"
 	"os"
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/pkg/plugin"
 )
 
diff --git a/pkg/plugin/installer/local_installer.go b/pkg/plugin/installer/local_installer.go
index 52636d019..109f4f236 100644
--- a/pkg/plugin/installer/local_installer.go
+++ b/pkg/plugin/installer/local_installer.go
@@ -16,11 +16,11 @@ limitations under the License.
 package installer // import "helm.sh/helm/v4/pkg/plugin/installer"
 
 import (
+	"errors"
+	"fmt"
 	"log/slog"
 	"os"
 	"path/filepath"
-
-	"github.com/pkg/errors"
 )
 
 // ErrPluginNotAFolder indicates that the plugin path is not a folder.
@@ -35,7 +35,7 @@ type LocalInstaller struct {
 func NewLocalInstaller(source string) (*LocalInstaller, error) {
 	src, err := filepath.Abs(source)
 	if err != nil {
-		return nil, errors.Wrap(err, "unable to get absolute path to plugin")
+		return nil, fmt.Errorf("unable to get absolute path to plugin: %w", err)
 	}
 	i := &LocalInstaller{
 		base: newBase(src),
diff --git a/pkg/plugin/installer/vcs_installer.go b/pkg/plugin/installer/vcs_installer.go
index d1b704d6e..3e53cbf11 100644
--- a/pkg/plugin/installer/vcs_installer.go
+++ b/pkg/plugin/installer/vcs_installer.go
@@ -16,13 +16,15 @@ limitations under the License.
 package installer // import "helm.sh/helm/v4/pkg/plugin/installer"
 
 import (
+	"errors"
+	"fmt"
+	stdfs "io/fs"
 	"log/slog"
 	"os"
 	"sort"
 
 	"github.com/Masterminds/semver/v3"
 	"github.com/Masterminds/vcs"
-	"github.com/pkg/errors"
 
 	"helm.sh/helm/v4/internal/third_party/dep/fs"
 	"helm.sh/helm/v4/pkg/helmpath"
@@ -145,7 +147,7 @@ func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) {
 		}
 	}
 
-	return "", errors.Errorf("requested version %q does not exist for plugin %q", i.Version, i.Repo.Remote())
+	return "", fmt.Errorf("requested version %q does not exist for plugin %q", i.Version, i.Repo.Remote())
 }
 
 // setVersion attempts to checkout the version
@@ -156,7 +158,7 @@ func (i *VCSInstaller) setVersion(repo vcs.Repo, ref string) error {
 
 // sync will clone or update a remote repo.
 func (i *VCSInstaller) sync(repo vcs.Repo) error {
-	if _, err := os.Stat(repo.LocalPath()); os.IsNotExist(err) {
+	if _, err := os.Stat(repo.LocalPath()); errors.Is(err, stdfs.ErrNotExist) {
 		slog.Debug("cloning", "source", repo.Remote(), "destination", repo.LocalPath())
 		return repo.Get()
 	}
diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go
index 3456664c1..9d79ab4fc 100644
--- a/pkg/plugin/plugin.go
+++ b/pkg/plugin/plugin.go
@@ -24,7 +24,6 @@ import (
 	"strings"
 	"unicode"
 
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	"helm.sh/helm/v4/pkg/cli"
@@ -313,12 +312,12 @@ func LoadDir(dirname string) (*Plugin, error) {
 	pluginfile := filepath.Join(dirname, PluginFileName)
 	data, err := os.ReadFile(pluginfile)
 	if err != nil {
-		return nil, errors.Wrapf(err, "failed to read plugin at %q", pluginfile)
+		return nil, fmt.Errorf("failed to read plugin at %q: %w", pluginfile, err)
 	}
 
 	plug := &Plugin{Dir: dirname}
 	if err := yaml.UnmarshalStrict(data, &plug.Metadata); err != nil {
-		return nil, errors.Wrapf(err, "failed to load plugin at %q", pluginfile)
+		return nil, fmt.Errorf("failed to load plugin at %q: %w", pluginfile, err)
 	}
 	return plug, validatePluginData(plug, pluginfile)
 }
@@ -332,7 +331,7 @@ func LoadAll(basedir string) ([]*Plugin, error) {
 	scanpath := filepath.Join(basedir, "*", PluginFileName)
 	matches, err := filepath.Glob(scanpath)
 	if err != nil {
-		return plugins, errors.Wrapf(err, "failed to find plugins in %q", scanpath)
+		return plugins, fmt.Errorf("failed to find plugins in %q: %w", scanpath, err)
 	}
 
 	if matches == nil {
diff --git a/pkg/postrender/exec.go b/pkg/postrender/exec.go
index 84357c656..16d9c09ce 100644
--- a/pkg/postrender/exec.go
+++ b/pkg/postrender/exec.go
@@ -18,11 +18,10 @@ package postrender
 
 import (
 	"bytes"
+	"fmt"
 	"io"
 	"os/exec"
 	"path/filepath"
-
-	"github.com/pkg/errors"
 )
 
 type execRender struct {
@@ -61,13 +60,13 @@ func (p *execRender) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error)
 	}()
 	err = cmd.Run()
 	if err != nil {
-		return nil, errors.Wrapf(err, "error while running command %s. error output:\n%s", p.binaryPath, stderr.String())
+		return nil, fmt.Errorf("error while running command %s. error output:\n%s: %w", p.binaryPath, stderr.String(), err)
 	}
 
 	// If the binary returned almost nothing, it's likely that it didn't
 	// successfully render anything
 	if len(bytes.TrimSpace(postRendered.Bytes())) == 0 {
-		return nil, errors.Errorf("post-renderer %q produced empty output", p.binaryPath)
+		return nil, fmt.Errorf("post-renderer %q produced empty output", p.binaryPath)
 	}
 
 	return postRendered, nil
@@ -95,7 +94,7 @@ func getFullPath(binaryPath string) (string, error) {
 	// 	// The plugins variable can actually contain multiple paths, so loop through those
 	// 	for _, p := range filepath.SplitList(pluginDir) {
 	// 		_, err := os.Stat(filepath.Join(p, binaryPath))
-	// 		if err != nil && !os.IsNotExist(err) {
+	// 		if err != nil && !errors.Is(err, fs.ErrNotExist) {
 	// 			return "", err
 	// 		} else if err == nil {
 	// 			binaryPath = filepath.Join(p, binaryPath)
@@ -108,7 +107,7 @@ func getFullPath(binaryPath string) (string, error) {
 	// the path and is executable
 	checkedPath, err := exec.LookPath(binaryPath)
 	if err != nil {
-		return "", errors.Wrapf(err, "unable to find binary at %s", binaryPath)
+		return "", fmt.Errorf("unable to find binary at %s: %w", binaryPath, err)
 	}
 
 	return filepath.Abs(checkedPath)
diff --git a/pkg/provenance/sign.go b/pkg/provenance/sign.go
index cd7664edd..504bc6aa1 100644
--- a/pkg/provenance/sign.go
+++ b/pkg/provenance/sign.go
@@ -19,12 +19,13 @@ import (
 	"bytes"
 	"crypto"
 	"encoding/hex"
+	"errors"
+	"fmt"
 	"io"
 	"os"
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
 	"golang.org/x/crypto/openpgp"           //nolint
 	"golang.org/x/crypto/openpgp/clearsign" //nolint
 	"golang.org/x/crypto/openpgp/packet"    //nolint
@@ -143,7 +144,7 @@ func NewFromKeyring(keyringfile, id string) (*Signatory, error) {
 		}
 	}
 	if vague {
-		return s, errors.Errorf("more than one key contain the id %q", id)
+		return s, fmt.Errorf("more than one key contain the id %q", id)
 	}
 
 	s.Entity = candidate
@@ -236,12 +237,12 @@ func (s *Signatory) ClearSign(chartpath string) (string, error) {
 		// In other words, if we call Close here, there's a risk that there's an attempt to use the
 		// private key to sign garbage data (since we know that io.Copy failed, `w` won't contain
 		// anything useful).
-		return "", errors.Wrap(err, "failed to write to clearsign encoder")
+		return "", fmt.Errorf("failed to write to clearsign encoder: %w", err)
 	}
 
 	err = w.Close()
 	if err != nil {
-		return "", errors.Wrap(err, "failed to either sign or armor message block")
+		return "", fmt.Errorf("failed to either sign or armor message block: %w", err)
 	}
 
 	return out.String(), nil
@@ -254,14 +255,14 @@ func (s *Signatory) Verify(chartpath, sigpath string) (*Verification, error) {
 		if fi, err := os.Stat(fname); err != nil {
 			return ver, err
 		} else if fi.IsDir() {
-			return ver, errors.Errorf("%s cannot be a directory", fname)
+			return ver, fmt.Errorf("%s cannot be a directory", fname)
 		}
 	}
 
 	// First verify the signature
 	sig, err := s.decodeSignature(sigpath)
 	if err != nil {
-		return ver, errors.Wrap(err, "failed to decode signature")
+		return ver, fmt.Errorf("failed to decode signature: %w", err)
 	}
 
 	by, err := s.verifySignature(sig)
@@ -283,9 +284,9 @@ func (s *Signatory) Verify(chartpath, sigpath string) (*Verification, error) {
 	sum = "sha256:" + sum
 	basename := filepath.Base(chartpath)
 	if sha, ok := sums.Files[basename]; !ok {
-		return ver, errors.Errorf("provenance does not contain a SHA for a file named %q", basename)
+		return ver, fmt.Errorf("provenance does not contain a SHA for a file named %q", basename)
 	} else if sha != sum {
-		return ver, errors.Errorf("sha256 sum does not match for %s: %q != %q", basename, sha, sum)
+		return ver, fmt.Errorf("sha256 sum does not match for %s: %q != %q", basename, sha, sum)
 	}
 	ver.FileHash = sum
 	ver.FileName = basename
diff --git a/pkg/pusher/ocipusher.go b/pkg/pusher/ocipusher.go
index 5cea78a44..699d27caf 100644
--- a/pkg/pusher/ocipusher.go
+++ b/pkg/pusher/ocipusher.go
@@ -16,7 +16,9 @@ limitations under the License.
 package pusher
 
 import (
+	"errors"
 	"fmt"
+	"io/fs"
 	"net"
 	"net/http"
 	"os"
@@ -24,8 +26,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/internal/tlsutil"
 	"helm.sh/helm/v4/pkg/chart/v2/loader"
 	"helm.sh/helm/v4/pkg/registry"
@@ -48,8 +48,8 @@ func (pusher *OCIPusher) Push(chartRef, href string, options ...Option) error {
 func (pusher *OCIPusher) push(chartRef, href string) error {
 	stat, err := os.Stat(chartRef)
 	if err != nil {
-		if os.IsNotExist(err) {
-			return errors.Errorf("%s: no such file", chartRef)
+		if errors.Is(err, fs.ErrNotExist) {
+			return fmt.Errorf("%s: no such file", chartRef)
 		}
 		return err
 	}
@@ -117,7 +117,7 @@ func (pusher *OCIPusher) newRegistryClient() (*registry.Client, error) {
 			tlsutil.WithCAFile(pusher.opts.caFile),
 		)
 		if err != nil {
-			return nil, errors.Wrap(err, "can't create TLS config for client")
+			return nil, fmt.Errorf("can't create TLS config for client: %w", err)
 		}
 
 		registryClient, err := registry.NewClient(
diff --git a/pkg/pusher/pusher.go b/pkg/pusher/pusher.go
index 0e07ad2d7..c4c766748 100644
--- a/pkg/pusher/pusher.go
+++ b/pkg/pusher/pusher.go
@@ -17,7 +17,7 @@ limitations under the License.
 package pusher
 
 import (
-	"github.com/pkg/errors"
+	"fmt"
 
 	"helm.sh/helm/v4/pkg/cli"
 	"helm.sh/helm/v4/pkg/registry"
@@ -106,7 +106,7 @@ func (p Providers) ByScheme(scheme string) (Pusher, error) {
 			return pp.New()
 		}
 	}
-	return nil, errors.Errorf("scheme %q not supported", scheme)
+	return nil, fmt.Errorf("scheme %q not supported", scheme)
 }
 
 var ociProvider = Provider{
diff --git a/pkg/registry/client.go b/pkg/registry/client.go
index 6c571ef4e..2d131dc47 100644
--- a/pkg/registry/client.go
+++ b/pkg/registry/client.go
@@ -21,6 +21,7 @@ import (
 	"crypto/tls"
 	"crypto/x509"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"net/http"
@@ -33,7 +34,6 @@ import (
 	"github.com/Masterminds/semver/v3"
 	"github.com/opencontainers/image-spec/specs-go"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
-	"github.com/pkg/errors"
 	"oras.land/oras-go/v2"
 	"oras.land/oras-go/v2/content"
 	"oras.land/oras-go/v2/content/memory"
@@ -463,7 +463,7 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
 			PreCopy: func(_ context.Context, desc ocispec.Descriptor) error {
 				mediaType := desc.MediaType
 				if i := sort.SearchStrings(allowedMediaTypes, mediaType); i >= len(allowedMediaTypes) || allowedMediaTypes[i] != mediaType {
-					return errors.Errorf("media type %q is not allowed, found in descriptor with digest: %q", mediaType, desc.Digest)
+					return fmt.Errorf("media type %q is not allowed, found in descriptor with digest: %q", mediaType, desc.Digest)
 				}
 
 				mu.Lock()
@@ -846,7 +846,7 @@ func (c *Client) ValidateReference(ref, version string, u *url.URL) (*url.URL, e
 		version = registryReference.Tag
 	} else {
 		if registryReference.Tag != "" && registryReference.Tag != version {
-			return nil, errors.Errorf("chart reference and version mismatch: %s is not %s", version, registryReference.Tag)
+			return nil, fmt.Errorf("chart reference and version mismatch: %s is not %s", version, registryReference.Tag)
 		}
 	}
 
@@ -865,7 +865,7 @@ func (c *Client) ValidateReference(ref, version string, u *url.URL) (*url.URL, e
 			return u, nil
 		}
 		if desc.Digest.String() != registryReference.Digest {
-			return nil, errors.Errorf("chart reference digest mismatch: %s is not %s", desc.Digest.String(), registryReference.Digest)
+			return nil, fmt.Errorf("chart reference digest mismatch: %s is not %s", desc.Digest.String(), registryReference.Digest)
 		}
 		return u, nil
 	}
@@ -881,7 +881,7 @@ func (c *Client) ValidateReference(ref, version string, u *url.URL) (*url.URL, e
 			return nil, err
 		}
 		if len(tags) == 0 {
-			return nil, errors.Errorf("Unable to locate any tags in provided repository: %s", ref)
+			return nil, fmt.Errorf("unable to locate any tags in provided repository: %s", ref)
 		}
 
 		// Determine if version provided
diff --git a/pkg/registry/util.go b/pkg/registry/util.go
index 1a96b0768..e63dda43a 100644
--- a/pkg/registry/util.go
+++ b/pkg/registry/util.go
@@ -31,7 +31,6 @@ import (
 
 	"github.com/Masterminds/semver/v3"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
-	"github.com/pkg/errors"
 )
 
 var immutableOciAnnotations = []string{
@@ -87,7 +86,7 @@ func GetTagMatchingVersionOrConstraint(tags []string, versionString string) (str
 		}
 	}
 
-	return "", errors.Errorf("Could not locate a version matching provided version string %s", versionString)
+	return "", fmt.Errorf("could not locate a version matching provided version string %s", versionString)
 }
 
 // extractChartMeta is used to extract a chart metadata from a byte array
diff --git a/pkg/release/util/manifest_sorter.go b/pkg/release/util/manifest_sorter.go
index df3bd71d7..be93ad1ed 100644
--- a/pkg/release/util/manifest_sorter.go
+++ b/pkg/release/util/manifest_sorter.go
@@ -17,13 +17,13 @@ limitations under the License.
 package util
 
 import (
+	"fmt"
 	"log/slog"
 	"path"
 	"sort"
 	"strconv"
 	"strings"
 
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
@@ -149,7 +149,7 @@ func (file *manifestFile) sort(result *result) error {
 
 		var entry SimpleHead
 		if err := yaml.Unmarshal([]byte(m), &entry); err != nil {
-			return errors.Wrapf(err, "YAML parse error on %s", file.path)
+			return fmt.Errorf("YAML parse error on %s: %w", file.path, err)
 		}
 
 		if !hasAnyAnnotation(entry) {
diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go
index 3b9f2bfea..e41226fa4 100644
--- a/pkg/repo/chartrepo.go
+++ b/pkg/repo/chartrepo.go
@@ -28,8 +28,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/pkg/getter"
 	"helm.sh/helm/v4/pkg/helmpath"
 )
@@ -59,12 +57,12 @@ type ChartRepository struct {
 func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository, error) {
 	u, err := url.Parse(cfg.URL)
 	if err != nil {
-		return nil, errors.Errorf("invalid chart URL format: %s", cfg.URL)
+		return nil, fmt.Errorf("invalid chart URL format: %s", cfg.URL)
 	}
 
 	client, err := getters.ByScheme(u.Scheme)
 	if err != nil {
-		return nil, errors.Errorf("could not find protocol handler for: %s", u.Scheme)
+		return nil, fmt.Errorf("could not find protocol handler for: %s", u.Scheme)
 	}
 
 	return &ChartRepository{
@@ -200,7 +198,7 @@ func FindChartInRepoURL(repoURL string, chartName string, getters getter.Provide
 	}
 	idx, err := r.DownloadIndexFile()
 	if err != nil {
-		return "", errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", repoURL)
+		return "", fmt.Errorf("looks like %q is not a valid chart repository or cannot be reached: %w", repoURL, err)
 	}
 	defer func() {
 		os.RemoveAll(filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name)))
@@ -226,14 +224,14 @@ func FindChartInRepoURL(repoURL string, chartName string, getters getter.Provide
 	}
 
 	if len(cv.URLs) == 0 {
-		return "", errors.Errorf("%s has no downloadable URLs", errMsg)
+		return "", fmt.Errorf("%s has no downloadable URLs", errMsg)
 	}
 
 	chartURL := cv.URLs[0]
 
 	absoluteChartURL, err := ResolveReferenceURL(repoURL, chartURL)
 	if err != nil {
-		return "", errors.Wrap(err, "failed to make chart URL absolute")
+		return "", fmt.Errorf("failed to make chart URL absolute: %w", err)
 	}
 
 	return absoluteChartURL, nil
@@ -244,7 +242,7 @@ func FindChartInRepoURL(repoURL string, chartName string, getters getter.Provide
 func ResolveReferenceURL(baseURL, refURL string) (string, error) {
 	parsedRefURL, err := url.Parse(refURL)
 	if err != nil {
-		return "", errors.Wrapf(err, "failed to parse %s as URL", refURL)
+		return "", fmt.Errorf("failed to parse %s as URL: %w", refURL, err)
 	}
 
 	if parsedRefURL.IsAbs() {
@@ -253,7 +251,7 @@ func ResolveReferenceURL(baseURL, refURL string) (string, error) {
 
 	parsedBaseURL, err := url.Parse(baseURL)
 	if err != nil {
-		return "", errors.Wrapf(err, "failed to parse %s as URL", baseURL)
+		return "", fmt.Errorf("failed to parse %s as URL: %w", baseURL, err)
 	}
 
 	// We need a trailing slash for ResolveReference to work, but make sure there isn't already one
diff --git a/pkg/repo/index.go b/pkg/repo/index.go
index 25a5f9886..c26d7581c 100644
--- a/pkg/repo/index.go
+++ b/pkg/repo/index.go
@@ -19,6 +19,8 @@ package repo
 import (
 	"bytes"
 	"encoding/json"
+	"errors"
+	"fmt"
 	"log/slog"
 	"os"
 	"path"
@@ -28,7 +30,6 @@ import (
 	"time"
 
 	"github.com/Masterminds/semver/v3"
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	"helm.sh/helm/v4/internal/fileutil"
@@ -108,7 +109,7 @@ func LoadIndexFile(path string) (*IndexFile, error) {
 	}
 	i, err := loadIndex(b, path)
 	if err != nil {
-		return nil, errors.Wrapf(err, "error loading %s", path)
+		return nil, fmt.Errorf("error loading %s: %w", path, err)
 	}
 	return i, nil
 }
@@ -124,7 +125,7 @@ func (i IndexFile) MustAdd(md *chart.Metadata, filename, baseURL, digest string)
 		md.APIVersion = chart.APIVersionV1
 	}
 	if err := md.Validate(); err != nil {
-		return errors.Wrapf(err, "validate failed for %s", filename)
+		return fmt.Errorf("validate failed for %s: %w", filename, err)
 	}
 
 	u := filename
@@ -217,7 +218,7 @@ func (i IndexFile) Get(name, version string) (*ChartVersion, error) {
 			return ver, nil
 		}
 	}
-	return nil, errors.Errorf("no chart version found for %s-%s", name, version)
+	return nil, fmt.Errorf("no chart version found for %s-%s", name, version)
 }
 
 // WriteFile writes an index file to the given destination path.
@@ -330,7 +331,7 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
 			return index, err
 		}
 		if err := index.MustAdd(c.Metadata, fname, parentURL, hash); err != nil {
-			return index, errors.Wrapf(err, "failed adding to %s to index", fname)
+			return index, fmt.Errorf("failed adding to %s to index: %w", fname, err)
 		}
 	}
 	return index, nil
diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go
index 203dfb4de..48c0e0193 100644
--- a/pkg/repo/repo.go
+++ b/pkg/repo/repo.go
@@ -17,11 +17,11 @@ limitations under the License.
 package repo // import "helm.sh/helm/v4/pkg/repo"
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
 	"time"
 
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 )
 
@@ -48,7 +48,7 @@ func LoadFile(path string) (*File, error) {
 	r := new(File)
 	b, err := os.ReadFile(path)
 	if err != nil {
-		return r, errors.Wrapf(err, "couldn't load repositories file (%s)", path)
+		return r, fmt.Errorf("couldn't load repositories file (%s): %w", path, err)
 	}
 
 	err = yaml.Unmarshal(b, r)
diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go
index ca9f0fa28..de097f294 100644
--- a/pkg/storage/driver/cfgmaps.go
+++ b/pkg/storage/driver/cfgmaps.go
@@ -24,7 +24,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -122,7 +121,7 @@ func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, err
 	ls := kblabels.Set{}
 	for k, v := range labels {
 		if errs := validation.IsValidLabelValue(v); len(errs) != 0 {
-			return nil, errors.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; "))
+			return nil, fmt.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; "))
 		}
 		ls[k] = v
 	}
diff --git a/pkg/storage/driver/driver.go b/pkg/storage/driver/driver.go
index 661c32e52..4f9d63928 100644
--- a/pkg/storage/driver/driver.go
+++ b/pkg/storage/driver/driver.go
@@ -17,10 +17,9 @@ limitations under the License.
 package driver // import "helm.sh/helm/v4/pkg/storage/driver"
 
 import (
+	"errors"
 	"fmt"
 
-	"github.com/pkg/errors"
-
 	rspb "helm.sh/helm/v4/pkg/release/v1"
 )
 
diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go
index 4af38a66e..23a8f5cab 100644
--- a/pkg/storage/driver/secrets.go
+++ b/pkg/storage/driver/secrets.go
@@ -24,7 +24,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -68,12 +67,15 @@ func (secrets *Secrets) Get(key string) (*rspb.Release, error) {
 		if apierrors.IsNotFound(err) {
 			return nil, ErrReleaseNotFound
 		}
-		return nil, errors.Wrapf(err, "get: failed to get %q", key)
+		return nil, fmt.Errorf("get: failed to get %q: %w", key, err)
 	}
 	// found the secret, decode the base64 data string
 	r, err := decodeRelease(string(obj.Data["release"]))
+	if err != nil {
+		return r, fmt.Errorf("get: failed to decode data %q: %w", key, err)
+	}
 	r.Labels = filterSystemLabels(obj.Labels)
-	return r, errors.Wrapf(err, "get: failed to decode data %q", key)
+	return r, nil
 }
 
 // List fetches all releases and returns the list releases such
@@ -85,7 +87,7 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release,
 
 	list, err := secrets.impl.List(context.Background(), opts)
 	if err != nil {
-		return nil, errors.Wrap(err, "list: failed to list")
+		return nil, fmt.Errorf("list: failed to list: %w", err)
 	}
 
 	var results []*rspb.Release
@@ -114,7 +116,7 @@ func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error)
 	ls := kblabels.Set{}
 	for k, v := range labels {
 		if errs := validation.IsValidLabelValue(v); len(errs) != 0 {
-			return nil, errors.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; "))
+			return nil, fmt.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; "))
 		}
 		ls[k] = v
 	}
@@ -123,7 +125,7 @@ func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error)
 
 	list, err := secrets.impl.List(context.Background(), opts)
 	if err != nil {
-		return nil, errors.Wrap(err, "query: failed to query with labels")
+		return nil, fmt.Errorf("query: failed to query with labels: %w", err)
 	}
 
 	if len(list.Items) == 0 {
@@ -156,7 +158,7 @@ func (secrets *Secrets) Create(key string, rls *rspb.Release) error {
 	// create a new secret to hold the release
 	obj, err := newSecretsObject(key, rls, lbs)
 	if err != nil {
-		return errors.Wrapf(err, "create: failed to encode release %q", rls.Name)
+		return fmt.Errorf("create: failed to encode release %q: %w", rls.Name, err)
 	}
 	// push the secret object out into the kubiverse
 	if _, err := secrets.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil {
@@ -164,7 +166,7 @@ func (secrets *Secrets) Create(key string, rls *rspb.Release) error {
 			return ErrReleaseExists
 		}
 
-		return errors.Wrap(err, "create: failed to create")
+		return fmt.Errorf("create: failed to create: %w", err)
 	}
 	return nil
 }
@@ -182,11 +184,14 @@ func (secrets *Secrets) Update(key string, rls *rspb.Release) error {
 	// create a new secret object to hold the release
 	obj, err := newSecretsObject(key, rls, lbs)
 	if err != nil {
-		return errors.Wrapf(err, "update: failed to encode release %q", rls.Name)
+		return fmt.Errorf("update: failed to encode release %q: %w", rls.Name, err)
 	}
 	// push the secret object out into the kubiverse
 	_, err = secrets.impl.Update(context.Background(), obj, metav1.UpdateOptions{})
-	return errors.Wrap(err, "update: failed to update")
+	if err != nil {
+		return fmt.Errorf("update: failed to update: %w", err)
+	}
+	return nil
 }
 
 // Delete deletes the Secret holding the release named by key.
@@ -197,7 +202,10 @@ func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) {
 	}
 	// delete the release
 	err = secrets.impl.Delete(context.Background(), key, metav1.DeleteOptions{})
-	return rls, err
+	if err != nil {
+		return nil, err
+	}
+	return rls, nil
 }
 
 // newSecretsObject constructs a kubernetes Secret object
diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go
index 927b33c44..b43f7c0f2 100644
--- a/pkg/storage/storage.go
+++ b/pkg/storage/storage.go
@@ -17,12 +17,11 @@ limitations under the License.
 package storage // import "helm.sh/helm/v4/pkg/storage"
 
 import (
+	"errors"
 	"fmt"
 	"log/slog"
 	"strings"
 
-	"github.com/pkg/errors"
-
 	relutil "helm.sh/helm/v4/pkg/release/util"
 	rspb "helm.sh/helm/v4/pkg/release/v1"
 	"helm.sh/helm/v4/pkg/storage/driver"
@@ -212,7 +211,7 @@ func (s *Storage) removeLeastRecent(name string, maximum int) error {
 	case 1:
 		return errs[0]
 	default:
-		return errors.Errorf("encountered %d deletion errors. First is: %s", c, errs[0])
+		return fmt.Errorf("encountered %d deletion errors. First is: %w", c, errs[0])
 	}
 }
 
@@ -234,7 +233,7 @@ func (s *Storage) Last(name string) (*rspb.Release, error) {
 		return nil, err
 	}
 	if len(h) == 0 {
-		return nil, errors.Errorf("no revision for release %q", name)
+		return nil, fmt.Errorf("no revision for release %q", name)
 	}
 
 	relutil.Reverse(h, relutil.SortByRevision)
diff --git a/pkg/strvals/literal_parser.go b/pkg/strvals/literal_parser.go
index f75655811..d34e5e854 100644
--- a/pkg/strvals/literal_parser.go
+++ b/pkg/strvals/literal_parser.go
@@ -20,8 +20,6 @@ import (
 	"fmt"
 	"io"
 	"strconv"
-
-	"github.com/pkg/errors"
 )
 
 // ParseLiteral parses a set line interpreting the value as a literal string.
@@ -102,7 +100,7 @@ func (t *literalParser) key(data map[string]interface{}, nestedNameLevel int) (r
 			if len(key) == 0 {
 				return err
 			}
-			return errors.Errorf("key %q has no value", string(key))
+			return fmt.Errorf("key %q has no value", string(key))
 
 		case lastRune == '=':
 			// found end of key: swallow the '=' and get the value
@@ -129,7 +127,7 @@ func (t *literalParser) key(data map[string]interface{}, nestedNameLevel int) (r
 			// recurse on sub-tree with remaining data
 			err := t.key(inner, nestedNameLevel)
 			if err == nil && len(inner) == 0 {
-				return errors.Errorf("key map %q has no value", string(key))
+				return fmt.Errorf("key map %q has no value", string(key))
 			}
 			if len(inner) != 0 {
 				set(data, string(key), inner)
@@ -140,7 +138,7 @@ func (t *literalParser) key(data map[string]interface{}, nestedNameLevel int) (r
 			// We are in a list index context, so we need to set an index.
 			i, err := t.keyIndex()
 			if err != nil {
-				return errors.Wrap(err, "error parsing index")
+				return fmt.Errorf("error parsing index: %w", err)
 			}
 			kk := string(key)
 
@@ -178,7 +176,7 @@ func (t *literalParser) listItem(list []interface{}, i, nestedNameLevel int) ([]
 
 	switch key, lastRune, err := runesUntilLiteral(t.sc, stop); {
 	case len(key) > 0:
-		return list, errors.Errorf("unexpected data at end of array index: %q", key)
+		return list, fmt.Errorf("unexpected data at end of array index: %q", key)
 
 	case err != nil:
 		return list, err
@@ -214,7 +212,7 @@ func (t *literalParser) listItem(list []interface{}, i, nestedNameLevel int) ([]
 		// now we have a nested list. Read the index and handle.
 		nextI, err := t.keyIndex()
 		if err != nil {
-			return list, errors.Wrap(err, "error parsing index")
+			return list, fmt.Errorf("error parsing index: %w", err)
 		}
 		var crtList []interface{}
 		if len(list) > i {
@@ -233,7 +231,7 @@ func (t *literalParser) listItem(list []interface{}, i, nestedNameLevel int) ([]
 		return setIndex(list, i, list2)
 
 	default:
-		return nil, errors.Errorf("parse error: unexpected token %v", lastRune)
+		return nil, fmt.Errorf("parse error: unexpected token %v", lastRune)
 	}
 }
 
diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go
index a0e8d66d1..c65e98c84 100644
--- a/pkg/strvals/parser.go
+++ b/pkg/strvals/parser.go
@@ -18,13 +18,13 @@ package strvals
 import (
 	"bytes"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"strconv"
 	"strings"
 	"unicode"
 
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 )
 
@@ -189,14 +189,14 @@ func (t *parser) key(data map[string]interface{}, nestedNameLevel int) (reterr e
 			if len(k) == 0 {
 				return err
 			}
-			return errors.Errorf("key %q has no value", string(k))
+			return fmt.Errorf("key %q has no value", string(k))
 			//set(data, string(k), "")
 			//return err
 		case last == '[':
 			// We are in a list index context, so we need to set an index.
 			i, err := t.keyIndex()
 			if err != nil {
-				return errors.Wrap(err, "error parsing index")
+				return fmt.Errorf("error parsing index: %w", err)
 			}
 			kk := string(k)
 			// Find or create target list
@@ -261,7 +261,7 @@ func (t *parser) key(data map[string]interface{}, nestedNameLevel int) (reterr e
 		case last == ',':
 			// No value given. Set the value to empty string. Return error.
 			set(data, string(k), "")
-			return errors.Errorf("key %q has no value (cannot end with ,)", string(k))
+			return fmt.Errorf("key %q has no value (cannot end with ,)", string(k))
 		case last == '.':
 			// Check value name is within the maximum nested name level
 			nestedNameLevel++
@@ -278,7 +278,7 @@ func (t *parser) key(data map[string]interface{}, nestedNameLevel int) (reterr e
 			// Recurse
 			e := t.key(inner, nestedNameLevel)
 			if e == nil && len(inner) == 0 {
-				return errors.Errorf("key map %q has no value", string(k))
+				return fmt.Errorf("key map %q has no value", string(k))
 			}
 			if len(inner) != 0 {
 				set(data, string(k), inner)
@@ -332,6 +332,7 @@ func (t *parser) keyIndex() (int, error) {
 	return strconv.Atoi(string(v))
 
 }
+
 func (t *parser) listItem(list []interface{}, i, nestedNameLevel int) ([]interface{}, error) {
 	if i < 0 {
 		return list, fmt.Errorf("negative %d index not allowed", i)
@@ -339,7 +340,7 @@ func (t *parser) listItem(list []interface{}, i, nestedNameLevel int) ([]interfa
 	stop := runeSet([]rune{'[', '.', '='})
 	switch k, last, err := runesUntil(t.sc, stop); {
 	case len(k) > 0:
-		return list, errors.Errorf("unexpected data at end of array index: %q", k)
+		return list, fmt.Errorf("unexpected data at end of array index: %q", k)
 	case err != nil:
 		return list, err
 	case last == '=':
@@ -394,7 +395,7 @@ func (t *parser) listItem(list []interface{}, i, nestedNameLevel int) ([]interfa
 		// now we have a nested list. Read the index and handle.
 		nextI, err := t.keyIndex()
 		if err != nil {
-			return list, errors.Wrap(err, "error parsing index")
+			return list, fmt.Errorf("error parsing index: %w", err)
 		}
 		var crtList []interface{}
 		if len(list) > i {
@@ -430,7 +431,7 @@ func (t *parser) listItem(list []interface{}, i, nestedNameLevel int) ([]interfa
 		}
 		return setIndex(list, i, inner)
 	default:
-		return nil, errors.Errorf("parse error: unexpected token %v", last)
+		return nil, fmt.Errorf("parse error: unexpected token %v", last)
 	}
 }
 
diff --git a/pkg/uploader/chart_uploader.go b/pkg/uploader/chart_uploader.go
index 41dfd4455..b3d612e38 100644
--- a/pkg/uploader/chart_uploader.go
+++ b/pkg/uploader/chart_uploader.go
@@ -20,8 +20,6 @@ import (
 	"io"
 	"net/url"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/pkg/pusher"
 	"helm.sh/helm/v4/pkg/registry"
 )
@@ -42,7 +40,7 @@ type ChartUploader struct {
 func (c *ChartUploader) UploadTo(ref, remote string) error {
 	u, err := url.Parse(remote)
 	if err != nil {
-		return errors.Errorf("invalid chart URL format: %s", remote)
+		return fmt.Errorf("invalid chart URL format: %s", remote)
 	}
 
 	if u.Scheme == "" {


Test patch:
diff --git a/internal/test/test.go b/internal/test/test.go
index e071d3160..632bc72fd 100644
--- a/internal/test/test.go
+++ b/internal/test/test.go
@@ -19,10 +19,9 @@ package test
 import (
 	"bytes"
 	"flag"
+	"fmt"
 	"os"
 	"path/filepath"
-
-	"github.com/pkg/errors"
 )
 
 // UpdateGolden writes out the golden files with the latest values, rather than failing the test.
@@ -75,11 +74,11 @@ func compare(actual []byte, filename string) error {
 
 	expected, err := os.ReadFile(filename)
 	if err != nil {
-		return errors.Wrapf(err, "unable to read testdata %s", filename)
+		return fmt.Errorf("unable to read testdata %s: %w", filename, err)
 	}
 	expected = normalize(expected)
 	if !bytes.Equal(expected, actual) {
-		return errors.Errorf("does not match golden file %s\n\nWANT:\n'%s'\n\nGOT:\n'%s'", filename, expected, actual)
+		return fmt.Errorf("does not match golden file %s\n\nWANT:\n'%s'\n\nGOT:\n'%s'", filename, expected, actual)
 	}
 	return nil
 }
diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go
index 56204651f..e39674c80 100644
--- a/pkg/action/install_test.go
+++ b/pkg/action/install_test.go
@@ -19,8 +19,10 @@ package action
 import (
 	"bytes"
 	"context"
+	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"net/http"
 	"os"
 	"path/filepath"
@@ -210,7 +212,7 @@ func TestInstallReleaseWithTakeOwnership_ResourceOwnedNoFlag(t *testing.T) {
 	instAction := installActionWithConfig(config)
 	_, err := instAction.Run(buildChart(), nil)
 	is.Error(err)
-	is.Contains(err.Error(), "Unable to continue with install")
+	is.Contains(err.Error(), "unable to continue with install")
 }
 
 func TestInstallReleaseWithValues(t *testing.T) {
@@ -756,7 +758,7 @@ func TestInstallReleaseOutputDir(t *testing.T) {
 	test.AssertGoldenFile(t, filepath.Join(dir, "hello/templates/rbac"), "rbac.txt")
 
 	_, err = os.Stat(filepath.Join(dir, "hello/templates/empty"))
-	is.True(os.IsNotExist(err))
+	is.True(errors.Is(err, fs.ErrNotExist))
 }
 
 func TestInstallOutputDirWithReleaseName(t *testing.T) {
@@ -792,7 +794,7 @@ func TestInstallOutputDirWithReleaseName(t *testing.T) {
 	test.AssertGoldenFile(t, filepath.Join(newDir, "hello/templates/rbac"), "rbac.txt")
 
 	_, err = os.Stat(filepath.Join(newDir, "hello/templates/empty"))
-	is.True(os.IsNotExist(err))
+	is.True(errors.Is(err, fs.ErrNotExist))
 }
 
 func TestNameAndChart(t *testing.T) {
diff --git a/pkg/action/release_testing.go b/pkg/action/release_testing.go
index 7edc3ed34..b5f6fe712 100644
--- a/pkg/action/release_testing.go
+++ b/pkg/action/release_testing.go
@@ -24,7 +24,6 @@ import (
 	"sort"
 	"time"
 
-	"github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
 
 	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
@@ -64,7 +63,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
 	}
 
 	if err := chartutil.ValidateReleaseName(name); err != nil {
-		return nil, errors.Errorf("releaseTest: Release name is invalid: %s", name)
+		return nil, fmt.Errorf("releaseTest: Release name is invalid: %s", name)
 	}
 
 	// finds the non-deleted release with the given name
@@ -113,7 +112,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
 func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
 	client, err := r.cfg.KubernetesClientSet()
 	if err != nil {
-		return errors.Wrap(err, "unable to get kubernetes client to fetch pod logs")
+		return fmt.Errorf("unable to get kubernetes client to fetch pod logs: %w", err)
 	}
 
 	hooksByWight := append([]*release.Hook{}, rel.Hooks...)
@@ -130,14 +129,14 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
 				req := client.CoreV1().Pods(r.Namespace).GetLogs(h.Name, &v1.PodLogOptions{})
 				logReader, err := req.Stream(context.Background())
 				if err != nil {
-					return errors.Wrapf(err, "unable to get pod logs for %s", h.Name)
+					return fmt.Errorf("unable to get pod logs for %s: %w", h.Name, err)
 				}
 
 				fmt.Fprintf(out, "POD LOGS: %s\n", h.Name)
 				_, err = io.Copy(out, logReader)
 				fmt.Fprintln(out)
 				if err != nil {
-					return errors.Wrapf(err, "unable to write pod logs for %s", h.Name)
+					return fmt.Errorf("unable to write pod logs for %s: %w", h.Name, err)
 				}
 			}
 		}
diff --git a/pkg/cmd/dependency_update_test.go b/pkg/cmd/dependency_update_test.go
index 890767720..a450d4b22 100644
--- a/pkg/cmd/dependency_update_test.go
+++ b/pkg/cmd/dependency_update_test.go
@@ -16,7 +16,9 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"fmt"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"strings"
@@ -202,7 +204,7 @@ func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
 
 	// Make sure tmpcharts-x is deleted
 	tmpPath := filepath.Join(dir(chartname), fmt.Sprintf("tmpcharts-%d", os.Getpid()))
-	if _, err := os.Stat(tmpPath); !os.IsNotExist(err) {
+	if _, err := os.Stat(tmpPath); !errors.Is(err, fs.ErrNotExist) {
 		t.Fatalf("tmpcharts dir still exists")
 	}
 }
diff --git a/pkg/cmd/repo_add_test.go b/pkg/cmd/repo_add_test.go
index 0f3a3de4f..05b5ee53e 100644
--- a/pkg/cmd/repo_add_test.go
+++ b/pkg/cmd/repo_add_test.go
@@ -17,8 +17,10 @@ limitations under the License.
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"strings"
@@ -113,11 +115,11 @@ func TestRepoAdd(t *testing.T) {
 	}
 
 	idx := filepath.Join(helmpath.CachePath("repository"), helmpath.CacheIndexFile(testRepoName))
-	if _, err := os.Stat(idx); os.IsNotExist(err) {
+	if _, err := os.Stat(idx); errors.Is(err, fs.ErrNotExist) {
 		t.Errorf("Error cache index file was not created for repository %s", testRepoName)
 	}
 	idx = filepath.Join(helmpath.CachePath("repository"), helmpath.CacheChartsFile(testRepoName))
-	if _, err := os.Stat(idx); os.IsNotExist(err) {
+	if _, err := os.Stat(idx); errors.Is(err, fs.ErrNotExist) {
 		t.Errorf("Error cache charts file was not created for repository %s", testRepoName)
 	}
 
diff --git a/pkg/cmd/testdata/output/install-hide-secret.txt b/pkg/cmd/testdata/output/install-hide-secret.txt
index aaf73b478..165f14f73 100644
--- a/pkg/cmd/testdata/output/install-hide-secret.txt
+++ b/pkg/cmd/testdata/output/install-hide-secret.txt
@@ -1,1 +1,1 @@
-Error: INSTALLATION FAILED: Hiding Kubernetes secrets requires a dry-run mode
+Error: INSTALLATION FAILED: hiding Kubernetes secrets requires a dry-run mode
diff --git a/pkg/cmd/testdata/output/upgrade-with-missing-dependencies.txt b/pkg/cmd/testdata/output/upgrade-with-missing-dependencies.txt
index adf2ae899..b2c154a80 100644
--- a/pkg/cmd/testdata/output/upgrade-with-missing-dependencies.txt
+++ b/pkg/cmd/testdata/output/upgrade-with-missing-dependencies.txt
@@ -1,1 +1,1 @@
-Error: An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies: found in Chart.yaml, but missing in charts/ directory: reqsubchart2
+Error: an error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies: found in Chart.yaml, but missing in charts/ directory: reqsubchart2
diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go
index b8b009f1b..fecc8fbef 100644
--- a/pkg/downloader/manager_test.go
+++ b/pkg/downloader/manager_test.go
@@ -17,6 +17,8 @@ package downloader
 
 import (
 	"bytes"
+	"errors"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -262,7 +264,7 @@ func TestDownloadAll(t *testing.T) {
 		t.Error(err)
 	}
 
-	if _, err := os.Stat(filepath.Join(chartPath, "charts", "signtest-0.1.0.tgz")); os.IsNotExist(err) {
+	if _, err := os.Stat(filepath.Join(chartPath, "charts", "signtest-0.1.0.tgz")); errors.Is(err, fs.ErrNotExist) {
 		t.Error(err)
 	}
 
diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go
index 27752a257..510fffd13 100644
--- a/pkg/getter/httpgetter_test.go
+++ b/pkg/getter/httpgetter_test.go
@@ -28,8 +28,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/internal/tlsutil"
 	"helm.sh/helm/v4/internal/version"
 	"helm.sh/helm/v4/pkg/cli"
@@ -317,7 +315,7 @@ func TestDownloadTLS(t *testing.T) {
 		tlsutil.WithCAFile(ca),
 	)
 	if err != nil {
-		t.Fatal(errors.Wrap(err, "can't create TLS config for client"))
+		t.Fatal(fmt.Errorf("can't create TLS config for client: %w", err))
 	}
 	tlsConf.ServerName = "helm.sh"
 	tlsSrv.TLS = tlsConf
@@ -372,7 +370,7 @@ func TestDownloadTLSWithRedirect(t *testing.T) {
 	)
 
 	if err != nil {
-		t.Fatal(errors.Wrap(err, "can't create TLS config for client"))
+		t.Fatal(fmt.Errorf("can't create TLS config for client: %w", err))
 	}
 
 	tlsSrv2 := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
@@ -393,7 +391,7 @@ func TestDownloadTLSWithRedirect(t *testing.T) {
 	)
 
 	if err != nil {
-		t.Fatal(errors.Wrap(err, "can't create TLS config for client"))
+		t.Fatal(fmt.Errorf("can't create TLS config for client: %w", err))
 	}
 
 	tlsSrv1 := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go
index 061d90e33..1c63a6d94 100644
--- a/pkg/lint/rules/chartfile_test.go
+++ b/pkg/lint/rules/chartfile_test.go
@@ -17,13 +17,12 @@ limitations under the License.
 package rules
 
 import (
+	"errors"
 	"os"
 	"path/filepath"
 	"strings"
 	"testing"
 
-	"github.com/pkg/errors"
-
 	chart "helm.sh/helm/v4/pkg/chart/v2"
 	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
 	"helm.sh/helm/v4/pkg/lint/support"
diff --git a/pkg/lint/support/message_test.go b/pkg/lint/support/message_test.go
index 9e12a638b..55675eeee 100644
--- a/pkg/lint/support/message_test.go
+++ b/pkg/lint/support/message_test.go
@@ -17,9 +17,8 @@ limitations under the License.
 package support
 
 import (
+	"errors"
 	"testing"
-
-	"github.com/pkg/errors"
 )
 
 var linter = Linter{}
diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go
index c5af1f2cc..ed4b73b35 100644
--- a/pkg/plugin/installer/http_installer_test.go
+++ b/pkg/plugin/installer/http_installer_test.go
@@ -20,7 +20,9 @@ import (
 	"bytes"
 	"compress/gzip"
 	"encoding/base64"
+	"errors"
 	"fmt"
+	"io/fs"
 	"net/http"
 	"net/http/httptest"
 	"os"
@@ -29,8 +31,6 @@ import (
 	"syscall"
 	"testing"
 
-	"github.com/pkg/errors"
-
 	"helm.sh/helm/v4/internal/test/ensure"
 	"helm.sh/helm/v4/pkg/getter"
 	"helm.sh/helm/v4/pkg/helmpath"
@@ -150,7 +150,7 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) {
 
 	// inject fake http client responding with error
 	httpInstaller.getter = &TestHTTPGetter{
-		MockError: errors.Errorf("failed to download plugin for some reason"),
+		MockError: fmt.Errorf("failed to download plugin for some reason"),
 	}
 
 	// attempt to install the plugin
@@ -276,7 +276,7 @@ func TestExtract(t *testing.T) {
 
 	pluginYAMLFullPath := filepath.Join(tempDir, "plugin.yaml")
 	if info, err := os.Stat(pluginYAMLFullPath); err != nil {
-		if os.IsNotExist(err) {
+		if errors.Is(err, fs.ErrNotExist) {
 			t.Fatalf("Expected %s to exist but doesn't", pluginYAMLFullPath)
 		}
 		t.Fatal(err)
@@ -286,7 +286,7 @@ func TestExtract(t *testing.T) {
 
 	readmeFullPath := filepath.Join(tempDir, "README.md")
 	if info, err := os.Stat(readmeFullPath); err != nil {
-		if os.IsNotExist(err) {
+		if errors.Is(err, fs.ErrNotExist) {
 			t.Fatalf("Expected %s to exist but doesn't", readmeFullPath)
 		}
 		t.Fatal(err)
diff --git a/pkg/storage/storage_test.go b/pkg/storage/storage_test.go
index f99d10214..d3025eca3 100644
--- a/pkg/storage/storage_test.go
+++ b/pkg/storage/storage_test.go
@@ -17,12 +17,11 @@ limitations under the License.
 package storage // import "helm.sh/helm/v4/pkg/storage"
 
 import (
+	"errors"
 	"fmt"
 	"reflect"
 	"testing"
 
-	"github.com/pkg/errors"
-
 	rspb "helm.sh/helm/v4/pkg/release/v1"
 	"helm.sh/helm/v4/pkg/storage/driver"
 )