diff --git a/.circleci/config.yml b/.circleci/config.yml
index 196ec6714366370a6bb462d114cb702f2f6770bd..ec061964339261324f163cd9c2b28a5622b033a6 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,7 +2,7 @@ version: 2
 jobs:
   fmt:
     docker:
-      - image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945
+      - image: nhooyr/websocket-ci@sha256:77e37211ded3c528e947439e294fbfc03b4fb9f9537c4e5198d5b304fd1df435
     steps:
       - checkout
       - restore_cache:
@@ -19,7 +19,7 @@ jobs:
 
   lint:
     docker:
-      - image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945
+      - image: nhooyr/websocket-ci@sha256:77e37211ded3c528e947439e294fbfc03b4fb9f9537c4e5198d5b304fd1df435
     steps:
       - checkout
       - restore_cache:
@@ -36,7 +36,7 @@ jobs:
 
   test:
     docker:
-      - image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945
+      - image: nhooyr/websocket-ci@sha256:77e37211ded3c528e947439e294fbfc03b4fb9f9537c4e5198d5b304fd1df435
     steps:
       - checkout
       - restore_cache:
diff --git a/README.md b/README.md
index 4ee0a30e80b0bdaa86450785e114d74d5ffff9c3..4091736d230dfc1e362c1c98425482fc5d5194cd 100644
--- a/README.md
+++ b/README.md
@@ -9,17 +9,17 @@ websocket is a minimal and idiomatic WebSocket library for Go.
 ## Install
 
 ```bash
-go get nhooyr.io/websocket@v1.5.0
+go get nhooyr.io/websocket@v1.5.1
 ```
 
 ## Features
 
 - Minimal and idiomatic API
 - Tiny codebase at 1700 lines
-- First class context.Context support
+- First class [context.Context](https://blog.golang.org/context) support
 - Thorough tests, fully passes the [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
-- Zero dependencies outside of the stdlib for the core library
-- JSON and ProtoBuf helpers in the wsjson and wspb subpackages
+- [Zero dependencies](https://godoc.org/nhooyr.io/websocket?imports)
+- JSON and ProtoBuf helpers in the [wsjson](https://godoc.org/nhooyr.io/websocket/wsjson) and [wspb](https://godoc.org/nhooyr.io/websocket/wspb) subpackages
 - Highly optimized by default
 - Concurrent writes out of the box
 
@@ -32,7 +32,7 @@ go get nhooyr.io/websocket@v1.5.0
 
 For a production quality example that shows off the full API, see the [echo example on the godoc](https://godoc.org/nhooyr.io/websocket#example-package--Echo). On github, the example is at [example_echo_test.go](./example_echo_test.go).
 
-Please use the [golang.org/x/xerrors.As](https://godoc.org/golang.org/x/xerrors#As) package to check for [websocket.CloseError](https://godoc.org/nhooyr.io/websocket#CloseError). See the [CloseError godoc example](https://godoc.org/nhooyr.io/websocket#example-CloseError).
+Please use the [errors.As](https://golang.org/pkg/errors/#As) function [new in Go 1.13](https://golang.org/doc/go1.13#error_wrapping) to check for [websocket.CloseError](https://godoc.org/nhooyr.io/websocket#CloseError). See the [CloseError godoc example](https://godoc.org/nhooyr.io/websocket#example-CloseError).
 
 ### Server
 
@@ -172,4 +172,4 @@ This is a list of companies or projects that use this library.
 
 - [Coder](https://github.com/cdr)
 
-If your company or project is using this library, please feel free to open a PR to amend the list.
+If your company or project is using this library, please feel free to open an issue or PR to amend the list.
diff --git a/accept.go b/accept.go
index afad1be2ec4771e6405688af706b9019fee9701e..7ef72ad2045502b9ffc0e6cc75ecddffeaf00809 100644
--- a/accept.go
+++ b/accept.go
@@ -4,14 +4,13 @@ import (
 	"bytes"
 	"crypto/sha1"
 	"encoding/base64"
+	"errors"
+	"fmt"
 	"io"
 	"net/http"
 	"net/textproto"
 	"net/url"
 	"strings"
-
-	"golang.org/x/net/http/httpguts"
-	"golang.org/x/xerrors"
 )
 
 // AcceptOptions represents the options available to pass to Accept.
@@ -43,31 +42,31 @@ type AcceptOptions struct {
 
 func verifyClientRequest(w http.ResponseWriter, r *http.Request) error {
 	if !headerValuesContainsToken(r.Header, "Connection", "Upgrade") {
-		err := xerrors.Errorf("websocket protocol violation: Connection header %q does not contain Upgrade", r.Header.Get("Connection"))
+		err := fmt.Errorf("websocket protocol violation: Connection header %q does not contain Upgrade", r.Header.Get("Connection"))
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		return err
 	}
 
 	if !headerValuesContainsToken(r.Header, "Upgrade", "WebSocket") {
-		err := xerrors.Errorf("websocket protocol violation: Upgrade header %q does not contain websocket", r.Header.Get("Upgrade"))
+		err := fmt.Errorf("websocket protocol violation: Upgrade header %q does not contain websocket", r.Header.Get("Upgrade"))
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		return err
 	}
 
 	if r.Method != "GET" {
-		err := xerrors.Errorf("websocket protocol violation: handshake request method is not GET but %q", r.Method)
+		err := fmt.Errorf("websocket protocol violation: handshake request method is not GET but %q", r.Method)
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		return err
 	}
 
 	if r.Header.Get("Sec-WebSocket-Version") != "13" {
-		err := xerrors.Errorf("unsupported websocket protocol version (only 13 is supported): %q", r.Header.Get("Sec-WebSocket-Version"))
+		err := fmt.Errorf("unsupported websocket protocol version (only 13 is supported): %q", r.Header.Get("Sec-WebSocket-Version"))
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		return err
 	}
 
 	if r.Header.Get("Sec-WebSocket-Key") == "" {
-		err := xerrors.New("websocket protocol violation: missing Sec-WebSocket-Key")
+		err := errors.New("websocket protocol violation: missing Sec-WebSocket-Key")
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		return err
 	}
@@ -87,7 +86,7 @@ func verifyClientRequest(w http.ResponseWriter, r *http.Request) error {
 func Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, error) {
 	c, err := accept(w, r, opts)
 	if err != nil {
-		return nil, xerrors.Errorf("failed to accept websocket connection: %w", err)
+		return nil, fmt.Errorf("failed to accept websocket connection: %w", err)
 	}
 	return c, nil
 }
@@ -112,7 +111,7 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn,
 
 	hj, ok := w.(http.Hijacker)
 	if !ok {
-		err = xerrors.New("passed ResponseWriter does not implement http.Hijacker")
+		err = errors.New("passed ResponseWriter does not implement http.Hijacker")
 		http.Error(w, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented)
 		return nil, err
 	}
@@ -131,7 +130,7 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn,
 
 	netConn, brw, err := hj.Hijack()
 	if err != nil {
-		err = xerrors.Errorf("failed to hijack connection: %w", err)
+		err = fmt.Errorf("failed to hijack connection: %w", err)
 		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
 		return nil, err
 	}
@@ -151,9 +150,29 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn,
 	return c, nil
 }
 
-func headerValuesContainsToken(h http.Header, key, val string) bool {
+func headerValuesContainsToken(h http.Header, key, token string) bool {
 	key = textproto.CanonicalMIMEHeaderKey(key)
-	return httpguts.HeaderValuesContainsToken(h[key], val)
+
+	for _, val2 := range h[key] {
+		if headerValueContainsToken(val2, token) {
+			return true
+		}
+	}
+
+	return false
+}
+
+func headerValueContainsToken(val2, token string) bool {
+	val2 = strings.TrimSpace(val2)
+
+	for _, val2 := range strings.Split(val2, ",") {
+		val2 = strings.TrimSpace(val2)
+		if strings.EqualFold(val2, token) {
+			return true
+		}
+	}
+
+	return false
 }
 
 func selectSubprotocol(r *http.Request, subprotocols []string) string {
@@ -187,10 +206,10 @@ func authenticateOrigin(r *http.Request) error {
 	}
 	u, err := url.Parse(origin)
 	if err != nil {
-		return xerrors.Errorf("failed to parse Origin header %q: %w", origin, err)
+		return fmt.Errorf("failed to parse Origin header %q: %w", origin, err)
 	}
 	if strings.EqualFold(u.Host, r.Host) {
 		return nil
 	}
-	return xerrors.Errorf("request Origin %q is not authorized for Host %q", origin, r.Host)
+	return fmt.Errorf("request Origin %q is not authorized for Host %q", origin, r.Host)
 }
diff --git a/dial.go b/dial.go
index 461817f60999744598ed243398b12a44b009a3ca..2ed836bd4422156ae0fb20ec626c36335338cb75 100644
--- a/dial.go
+++ b/dial.go
@@ -5,6 +5,7 @@ import (
 	"bytes"
 	"context"
 	"encoding/base64"
+	"fmt"
 	"io"
 	"io/ioutil"
 	"math/rand"
@@ -12,8 +13,6 @@ import (
 	"net/url"
 	"strings"
 	"sync"
-
-	"golang.org/x/xerrors"
 )
 
 // DialOptions represents the options available to pass to Dial.
@@ -44,7 +43,7 @@ type DialOptions struct {
 func Dial(ctx context.Context, u string, opts *DialOptions) (*Conn, *http.Response, error) {
 	c, r, err := dial(ctx, u, opts)
 	if err != nil {
-		return nil, r, xerrors.Errorf("failed to websocket dial: %w", err)
+		return nil, r, fmt.Errorf("failed to websocket dial: %w", err)
 	}
 	return c, r, nil
 }
@@ -62,7 +61,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re
 		opts.HTTPClient = http.DefaultClient
 	}
 	if opts.HTTPClient.Timeout > 0 {
-		return nil, nil, xerrors.Errorf("please use context for cancellation instead of http.Client.Timeout; see https://github.com/nhooyr/websocket/issues/67")
+		return nil, nil, fmt.Errorf("please use context for cancellation instead of http.Client.Timeout; see https://github.com/nhooyr/websocket/issues/67")
 	}
 	if opts.HTTPHeader == nil {
 		opts.HTTPHeader = http.Header{}
@@ -70,7 +69,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re
 
 	parsedURL, err := url.Parse(u)
 	if err != nil {
-		return nil, nil, xerrors.Errorf("failed to parse url: %w", err)
+		return nil, nil, fmt.Errorf("failed to parse url: %w", err)
 	}
 
 	switch parsedURL.Scheme {
@@ -79,7 +78,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re
 	case "wss":
 		parsedURL.Scheme = "https"
 	default:
-		return nil, nil, xerrors.Errorf("unexpected url scheme: %q", parsedURL.Scheme)
+		return nil, nil, fmt.Errorf("unexpected url scheme: %q", parsedURL.Scheme)
 	}
 
 	req, _ := http.NewRequest("GET", parsedURL.String(), nil)
@@ -95,7 +94,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re
 
 	resp, err := opts.HTTPClient.Do(req)
 	if err != nil {
-		return nil, nil, xerrors.Errorf("failed to send handshake request: %w", err)
+		return nil, nil, fmt.Errorf("failed to send handshake request: %w", err)
 	}
 	defer func() {
 		if err != nil {
@@ -114,7 +113,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re
 
 	rwc, ok := resp.Body.(io.ReadWriteCloser)
 	if !ok {
-		return nil, resp, xerrors.Errorf("response body is not a io.ReadWriteCloser: %T", rwc)
+		return nil, resp, fmt.Errorf("response body is not a io.ReadWriteCloser: %T", rwc)
 	}
 
 	c := &Conn{
@@ -132,19 +131,19 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re
 
 func verifyServerResponse(r *http.Request, resp *http.Response) error {
 	if resp.StatusCode != http.StatusSwitchingProtocols {
-		return xerrors.Errorf("expected handshake response status code %v but got %v", http.StatusSwitchingProtocols, resp.StatusCode)
+		return fmt.Errorf("expected handshake response status code %v but got %v", http.StatusSwitchingProtocols, resp.StatusCode)
 	}
 
 	if !headerValuesContainsToken(resp.Header, "Connection", "Upgrade") {
-		return xerrors.Errorf("websocket protocol violation: Connection header %q does not contain Upgrade", resp.Header.Get("Connection"))
+		return fmt.Errorf("websocket protocol violation: Connection header %q does not contain Upgrade", resp.Header.Get("Connection"))
 	}
 
 	if !headerValuesContainsToken(resp.Header, "Upgrade", "WebSocket") {
-		return xerrors.Errorf("websocket protocol violation: Upgrade header %q does not contain websocket", resp.Header.Get("Upgrade"))
+		return fmt.Errorf("websocket protocol violation: Upgrade header %q does not contain websocket", resp.Header.Get("Upgrade"))
 	}
 
 	if resp.Header.Get("Sec-WebSocket-Accept") != secWebSocketAccept(r.Header.Get("Sec-WebSocket-Key")) {
-		return xerrors.Errorf("websocket protocol violation: invalid Sec-WebSocket-Accept %q, key %q",
+		return fmt.Errorf("websocket protocol violation: invalid Sec-WebSocket-Accept %q, key %q",
 			resp.Header.Get("Sec-WebSocket-Accept"),
 			r.Header.Get("Sec-WebSocket-Key"),
 		)
diff --git a/example_echo_test.go b/example_echo_test.go
index 3e7e7f9da7a711238ad4f216e2cde9d5087cfce7..aad326756e03a362d4001b41e266a31e6723c447 100644
--- a/example_echo_test.go
+++ b/example_echo_test.go
@@ -10,7 +10,6 @@ import (
 	"time"
 
 	"golang.org/x/time/rate"
-	"golang.org/x/xerrors"
 
 	"nhooyr.io/websocket"
 	"nhooyr.io/websocket/wsjson"
@@ -78,14 +77,14 @@ func echoServer(w http.ResponseWriter, r *http.Request) error {
 
 	if c.Subprotocol() != "echo" {
 		c.Close(websocket.StatusPolicyViolation, "client must speak the echo subprotocol")
-		return xerrors.Errorf("client does not speak echo sub protocol")
+		return fmt.Errorf("client does not speak echo sub protocol")
 	}
 
 	l := rate.NewLimiter(rate.Every(time.Millisecond*100), 10)
 	for {
 		err = echo(r.Context(), c, l)
 		if err != nil {
-			return xerrors.Errorf("failed to echo with %v: %w", r.RemoteAddr, err)
+			return fmt.Errorf("failed to echo with %v: %w", r.RemoteAddr, err)
 		}
 	}
 }
@@ -114,7 +113,7 @@ func echo(ctx context.Context, c *websocket.Conn, l *rate.Limiter) error {
 
 	_, err = io.Copy(w, r)
 	if err != nil {
-		return xerrors.Errorf("failed to io.Copy: %w", err)
+		return fmt.Errorf("failed to io.Copy: %w", err)
 	}
 
 	err = w.Close()
diff --git a/example_test.go b/example_test.go
index 797658b47059e4e6ba152eebbfbe4972886fa253..36cab2bd6b3698c9bd97f1781e6eb37347437899 100644
--- a/example_test.go
+++ b/example_test.go
@@ -2,12 +2,11 @@ package websocket_test
 
 import (
 	"context"
+	"errors"
 	"log"
 	"net/http"
 	"time"
 
-	"golang.org/x/xerrors"
-
 	"nhooyr.io/websocket"
 	"nhooyr.io/websocket/wsjson"
 )
@@ -76,7 +75,7 @@ func ExampleCloseError() {
 
 	_, _, err = c.Reader(ctx)
 	var cerr websocket.CloseError
-	if !xerrors.As(err, &cerr) || cerr.Code != websocket.StatusNormalClosure {
+	if !errors.As(err, &cerr) || cerr.Code != websocket.StatusNormalClosure {
 		log.Fatalf("expected to be disconnected with StatusNormalClosure but got: %+v", err)
 		return
 	}
diff --git a/export_test.go b/export_test.go
index 811bf800b94011740d6a9f491f9ff8f9bfd8be8b..5a0d1c32482aee5d69f9c6362c46ce3a1bcb1340 100644
--- a/export_test.go
+++ b/export_test.go
@@ -3,8 +3,7 @@ package websocket
 import (
 	"bufio"
 	"context"
-
-	"golang.org/x/xerrors"
+	"fmt"
 )
 
 type (
@@ -65,7 +64,7 @@ func (c *Conn) WriteHeader(ctx context.Context, h Header) error {
 	})
 	_, err := c.bw.Write(headerBytes)
 	if err != nil {
-		return xerrors.Errorf("failed to write header: %w", err)
+		return fmt.Errorf("failed to write header: %w", err)
 	}
 	if h.Fin {
 		err = c.Flush()
diff --git a/go.mod b/go.mod
index 70fe1d4c111e94136ef01f194866cf2e86228d77..b59397c1ece37f41d008c5ac3b777e22f130fa7e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module nhooyr.io/websocket
 
-go 1.12
+go 1.13
 
 require (
 	github.com/fatih/color v1.7.0 // indirect
@@ -18,12 +18,9 @@ require (
 	go.uber.org/atomic v1.4.0 // indirect
 	go.uber.org/multierr v1.1.0
 	golang.org/x/lint v0.0.0-20190409202823-959b441ac422
-	golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
 	golang.org/x/sys v0.0.0-20190830142957-1e83adbbebd0 // indirect
-	golang.org/x/text v0.3.2 // indirect
 	golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
 	golang.org/x/tools v0.0.0-20190830223141-573d9926052a
-	golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
 	gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
 	gotest.tools v2.2.0+incompatible // indirect
 	gotest.tools/gotestsum v0.3.6-0.20190825182939-fc6cb5870c52
diff --git a/go.sum b/go.sum
index 906f9c38cb955e94ef5e56445b685b9959199d7f..18b3dd70751a82d53d34685dde090197e2663d98 100644
--- a/go.sum
+++ b/go.sum
@@ -78,8 +78,6 @@ golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -93,11 +91,8 @@ golang.org/x/sys v0.0.0-20190830142957-1e83adbbebd0 h1:7z820YPX9pxWR59qM7BE5+fgl
 golang.org/x/sys v0.0.0-20190830142957-1e83adbbebd0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190830223141-573d9926052a h1:XAHT1kdPpnU8Hk+FPi42KZFhtNFEk4vBg1U4OmIeHTU=
 golang.org/x/tools v0.0.0-20190830223141-573d9926052a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
diff --git a/header.go b/header.go
index 16ab6474e69c46b10bea83df98828c35482da032..6eb8610f5a65c067a581afab3214dd3ee79a57e4 100644
--- a/header.go
+++ b/header.go
@@ -5,8 +5,6 @@ import (
 	"fmt"
 	"io"
 	"math"
-
-	"golang.org/x/xerrors"
 )
 
 // First byte contains fin, rsv1, rsv2, rsv3.
@@ -145,7 +143,7 @@ func readHeader(b []byte, r io.Reader) (header, error) {
 	case payloadLength == 127:
 		h.payloadLength = int64(binary.BigEndian.Uint64(b))
 		if h.payloadLength < 0 {
-			return header{}, xerrors.Errorf("header with negative payload length: %v", h.payloadLength)
+			return header{}, fmt.Errorf("header with negative payload length: %v", h.payloadLength)
 		}
 		b = b[8:]
 	}
diff --git a/netconn.go b/netconn.go
index a7c9bf7fcf6a444deac547fa84ab89e3ee28ca82..20b99c2a69d531245263235fe36db69f26659a0a 100644
--- a/netconn.go
+++ b/netconn.go
@@ -2,13 +2,12 @@ package websocket
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"io"
 	"math"
 	"net"
 	"time"
-
-	"golang.org/x/xerrors"
 )
 
 // NetConn converts a *websocket.Conn into a net.Conn.
@@ -97,7 +96,7 @@ func (c *netConn) Read(p []byte) (int, error) {
 		typ, r, err := c.c.Reader(c.readContext)
 		if err != nil {
 			var ce CloseError
-			if xerrors.As(err, &ce) && (ce.Code == StatusNormalClosure) || (ce.Code == StatusGoingAway) {
+			if errors.As(err, &ce) && (ce.Code == StatusNormalClosure) || (ce.Code == StatusGoingAway) {
 				c.eofed = true
 				return 0, io.EOF
 			}
diff --git a/statuscode.go b/statuscode.go
index 558610d4a9bf9d571d6efd5a34315c649b1b97ff..d2a64d62e8be495b779e9241dadc0995abde98c3 100644
--- a/statuscode.go
+++ b/statuscode.go
@@ -3,8 +3,6 @@ package websocket
 import (
 	"encoding/binary"
 	"fmt"
-
-	"golang.org/x/xerrors"
 )
 
 // StatusCode represents a WebSocket status code.
@@ -43,7 +41,8 @@ const (
 // CloseError represents a WebSocket close frame.
 // It is returned by Conn's methods when a WebSocket close frame is received from
 // the peer.
-// You will need to use https://godoc.org/golang.org/x/xerrors#As to check for this error.
+// You will need to use the https://golang.org/pkg/errors/#As function, new in Go 1.13,
+// to check for this error. See the CloseError example.
 type CloseError struct {
 	Code   StatusCode
 	Reason string
@@ -61,7 +60,7 @@ func parseClosePayload(p []byte) (CloseError, error) {
 	}
 
 	if len(p) < 2 {
-		return CloseError{}, xerrors.Errorf("close payload %q too small, cannot even contain the 2 byte status code", p)
+		return CloseError{}, fmt.Errorf("close payload %q too small, cannot even contain the 2 byte status code", p)
 	}
 
 	ce := CloseError{
@@ -70,7 +69,7 @@ func parseClosePayload(p []byte) (CloseError, error) {
 	}
 
 	if !validWireCloseCode(ce.Code) {
-		return CloseError{}, xerrors.Errorf("invalid status code %v", ce.Code)
+		return CloseError{}, fmt.Errorf("invalid status code %v", ce.Code)
 	}
 
 	return ce, nil
@@ -98,10 +97,10 @@ const maxControlFramePayload = 125
 
 func (ce CloseError) bytes() ([]byte, error) {
 	if len(ce.Reason) > maxControlFramePayload-2 {
-		return nil, xerrors.Errorf("reason string max is %v but got %q with length %v", maxControlFramePayload-2, ce.Reason, len(ce.Reason))
+		return nil, fmt.Errorf("reason string max is %v but got %q with length %v", maxControlFramePayload-2, ce.Reason, len(ce.Reason))
 	}
 	if !validWireCloseCode(ce.Code) {
-		return nil, xerrors.Errorf("status code %v cannot be set", ce.Code)
+		return nil, fmt.Errorf("status code %v cannot be set", ce.Code)
 	}
 
 	buf := make([]byte, 2+len(ce.Reason))
diff --git a/websocket.go b/websocket.go
index 7dabfa25f686df3de346a2dcddb86dc527bdd315..56aca7051712d0fb330a7a33a432ab4fcc2ab00b 100644
--- a/websocket.go
+++ b/websocket.go
@@ -4,6 +4,7 @@ import (
 	"bufio"
 	"context"
 	cryptorand "crypto/rand"
+	"errors"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -14,8 +15,6 @@ import (
 	"sync"
 	"sync/atomic"
 	"time"
-
-	"golang.org/x/xerrors"
 )
 
 // Conn represents a WebSocket connection.
@@ -107,7 +106,7 @@ func (c *Conn) init() {
 	c.controlPayloadBuf = make([]byte, maxControlFramePayload)
 
 	runtime.SetFinalizer(c, func(c *Conn) {
-		c.close(xerrors.New("connection garbage collected"))
+		c.close(errors.New("connection garbage collected"))
 	})
 
 	go c.timeoutLoop()
@@ -121,7 +120,7 @@ func (c *Conn) Subprotocol() string {
 
 func (c *Conn) setCloseErr(err error) {
 	c.closeErrOnce.Do(func() {
-		c.closeErr = xerrors.Errorf("websocket closed: %w", err)
+		c.closeErr = fmt.Errorf("websocket closed: %w", err)
 	})
 }
 
@@ -166,9 +165,9 @@ func (c *Conn) timeoutLoop() {
 		case readCtx = <-c.setReadTimeout:
 
 		case <-readCtx.Done():
-			c.close(xerrors.Errorf("read timed out: %w", readCtx.Err()))
+			c.close(fmt.Errorf("read timed out: %w", readCtx.Err()))
 		case <-writeCtx.Done():
-			c.close(xerrors.Errorf("write timed out: %w", writeCtx.Err()))
+			c.close(fmt.Errorf("write timed out: %w", writeCtx.Err()))
 		}
 	}
 }
@@ -179,9 +178,9 @@ func (c *Conn) acquireLock(ctx context.Context, lock chan struct{}) error {
 		var err error
 		switch lock {
 		case c.writeFrameLock, c.writeMsgLock:
-			err = xerrors.Errorf("could not acquire write lock: %v", ctx.Err())
+			err = fmt.Errorf("could not acquire write lock: %v", ctx.Err())
 		case c.readFrameLock:
-			err = xerrors.Errorf("could not acquire read lock: %v", ctx.Err())
+			err = fmt.Errorf("could not acquire read lock: %v", ctx.Err())
 		default:
 			panic(fmt.Sprintf("websocket: failed to acquire unknown lock: %v", ctx.Err()))
 		}
@@ -217,7 +216,7 @@ func (c *Conn) readTillMsg(ctx context.Context) (header, error) {
 		if h.opcode.controlOp() {
 			err = c.handleControl(ctx, h)
 			if err != nil {
-				return header{}, xerrors.Errorf("failed to handle control frame: %w", err)
+				return header{}, fmt.Errorf("failed to handle control frame: %w", err)
 			}
 			continue
 		}
@@ -254,7 +253,7 @@ func (c *Conn) readFrameHeader(ctx context.Context) (header, error) {
 			err = ctx.Err()
 		default:
 		}
-		err := xerrors.Errorf("failed to read header: %w", err)
+		err := fmt.Errorf("failed to read header: %w", err)
 		c.releaseLock(c.readFrameLock)
 		c.close(err)
 		return header{}, err
@@ -307,14 +306,14 @@ func (c *Conn) handleControl(ctx context.Context, h header) error {
 	case opClose:
 		ce, err := parseClosePayload(b)
 		if err != nil {
-			err = xerrors.Errorf("received invalid close payload: %w", err)
+			err = fmt.Errorf("received invalid close payload: %w", err)
 			c.Close(StatusProtocolError, err.Error())
 			return c.closeErr
 		}
 		// This ensures the closeErr of the Conn is always the received CloseError
 		// in case the echo close frame write fails.
 		// See https://github.com/nhooyr/websocket/issues/109
-		c.setCloseErr(xerrors.Errorf("received close frame: %w", ce))
+		c.setCloseErr(fmt.Errorf("received close frame: %w", ce))
 		c.writeClose(b, nil)
 		return c.closeErr
 	default:
@@ -347,12 +346,12 @@ func (c *Conn) handleControl(ctx context.Context, h header) error {
 // Most users should not need this.
 func (c *Conn) Reader(ctx context.Context) (MessageType, io.Reader, error) {
 	if atomic.LoadInt64(&c.readClosed) == 1 {
-		return 0, nil, xerrors.Errorf("websocket connection read closed")
+		return 0, nil, fmt.Errorf("websocket connection read closed")
 	}
 
 	typ, r, err := c.reader(ctx)
 	if err != nil {
-		return 0, nil, xerrors.Errorf("failed to get reader: %w", err)
+		return 0, nil, fmt.Errorf("failed to get reader: %w", err)
 	}
 	return typ, r, nil
 }
@@ -363,7 +362,7 @@ func (c *Conn) reader(ctx context.Context) (MessageType, io.Reader, error) {
 		// if there is an active frame not yet fully read.
 		// Otherwise, a user may have read the last byte but not the EOF if the EOF
 		// is in the next frame so we check for that below.
-		return 0, nil, xerrors.Errorf("previous message not read to completion")
+		return 0, nil, fmt.Errorf("previous message not read to completion")
 	}
 
 	h, err := c.readTillMsg(ctx)
@@ -378,7 +377,7 @@ func (c *Conn) reader(ctx context.Context) (MessageType, io.Reader, error) {
 		}
 
 		if !h.fin || h.payloadLength > 0 {
-			return 0, nil, xerrors.Errorf("previous message not read to completion")
+			return 0, nil, fmt.Errorf("previous message not read to completion")
 		}
 
 		c.activeReader = nil
@@ -441,17 +440,17 @@ func (r *messageReader) Read(p []byte) (int, error) {
 	if err != nil {
 		// Have to return io.EOF directly for now, we cannot wrap as xerrors
 		// isn't used in stdlib.
-		if xerrors.Is(err, io.EOF) {
+		if errors.Is(err, io.EOF) {
 			return n, io.EOF
 		}
-		return n, xerrors.Errorf("failed to read: %w", err)
+		return n, fmt.Errorf("failed to read: %w", err)
 	}
 	return n, nil
 }
 
 func (r *messageReader) read(p []byte) (int, error) {
 	if r.eof() {
-		return 0, xerrors.Errorf("cannot use EOFed reader")
+		return 0, fmt.Errorf("cannot use EOFed reader")
 	}
 
 	if r.c.readMsgLeft <= 0 {
@@ -531,7 +530,7 @@ func (c *Conn) readFramePayload(ctx context.Context, p []byte) (int, error) {
 			err = ctx.Err()
 		default:
 		}
-		err = xerrors.Errorf("failed to read frame payload: %w", err)
+		err = fmt.Errorf("failed to read frame payload: %w", err)
 		c.releaseLock(c.readFrameLock)
 		c.close(err)
 		return n, err
@@ -580,7 +579,7 @@ func (c *Conn) Read(ctx context.Context) (MessageType, []byte, error) {
 func (c *Conn) Writer(ctx context.Context, typ MessageType) (io.WriteCloser, error) {
 	wc, err := c.writer(ctx, typ)
 	if err != nil {
-		return nil, xerrors.Errorf("failed to get writer: %w", err)
+		return nil, fmt.Errorf("failed to get writer: %w", err)
 	}
 	return wc, nil
 }
@@ -605,7 +604,7 @@ func (c *Conn) writer(ctx context.Context, typ MessageType) (io.WriteCloser, err
 func (c *Conn) Write(ctx context.Context, typ MessageType, p []byte) error {
 	_, err := c.write(ctx, typ, p)
 	if err != nil {
-		return xerrors.Errorf("failed to write msg: %w", err)
+		return fmt.Errorf("failed to write msg: %w", err)
 	}
 	return nil
 }
@@ -634,18 +633,18 @@ func (w *messageWriter) closed() bool {
 func (w *messageWriter) Write(p []byte) (int, error) {
 	n, err := w.write(p)
 	if err != nil {
-		return n, xerrors.Errorf("failed to write: %w", err)
+		return n, fmt.Errorf("failed to write: %w", err)
 	}
 	return n, nil
 }
 
 func (w *messageWriter) write(p []byte) (int, error) {
 	if w.closed() {
-		return 0, xerrors.Errorf("cannot use closed writer")
+		return 0, fmt.Errorf("cannot use closed writer")
 	}
 	n, err := w.c.writeFrame(w.c.writeMsgCtx, false, w.c.writeMsgOpcode, p)
 	if err != nil {
-		return n, xerrors.Errorf("failed to write data frame: %w", err)
+		return n, fmt.Errorf("failed to write data frame: %w", err)
 	}
 	w.c.writeMsgOpcode = opContinuation
 	return n, nil
@@ -656,20 +655,20 @@ func (w *messageWriter) write(p []byte) (int, error) {
 func (w *messageWriter) Close() error {
 	err := w.close()
 	if err != nil {
-		return xerrors.Errorf("failed to close writer: %w", err)
+		return fmt.Errorf("failed to close writer: %w", err)
 	}
 	return nil
 }
 
 func (w *messageWriter) close() error {
 	if w.closed() {
-		return xerrors.Errorf("cannot use closed writer")
+		return fmt.Errorf("cannot use closed writer")
 	}
 	w.c.activeWriter = nil
 
 	_, err := w.c.writeFrame(w.c.writeMsgCtx, true, w.c.writeMsgOpcode, nil)
 	if err != nil {
-		return xerrors.Errorf("failed to write fin frame: %w", err)
+		return fmt.Errorf("failed to write fin frame: %w", err)
 	}
 
 	w.c.releaseLock(w.c.writeMsgLock)
@@ -679,7 +678,7 @@ func (w *messageWriter) close() error {
 func (c *Conn) writeControl(ctx context.Context, opcode opcode, p []byte) error {
 	_, err := c.writeFrame(ctx, true, opcode, p)
 	if err != nil {
-		return xerrors.Errorf("failed to write control frame: %w", err)
+		return fmt.Errorf("failed to write control frame: %w", err)
 	}
 	return nil
 }
@@ -706,7 +705,7 @@ func (c *Conn) writeFrame(ctx context.Context, fin bool, opcode opcode, p []byte
 	if c.client {
 		_, err := io.ReadFull(cryptorand.Reader, c.writeHeader.maskKey[:])
 		if err != nil {
-			return 0, xerrors.Errorf("failed to generate masking key: %w", err)
+			return 0, fmt.Errorf("failed to generate masking key: %w", err)
 		}
 	}
 
@@ -737,7 +736,7 @@ func (c *Conn) realWriteFrame(ctx context.Context, h header, p []byte) (n int, e
 			default:
 			}
 
-			err = xerrors.Errorf("failed to write %v frame: %w", h.opcode, err)
+			err = fmt.Errorf("failed to write %v frame: %w", h.opcode, err)
 			// We need to release the lock first before closing the connection to ensure
 			// the lock can be acquired inside close to ensure no one can access c.bw.
 			c.releaseLock(c.writeFrameLock)
@@ -821,7 +820,7 @@ func (c *Conn) writePong(p []byte) error {
 func (c *Conn) Close(code StatusCode, reason string) error {
 	err := c.exportedClose(code, reason)
 	if err != nil {
-		return xerrors.Errorf("failed to close websocket connection: %w", err)
+		return fmt.Errorf("failed to close websocket connection: %w", err)
 	}
 	return nil
 }
@@ -846,13 +845,13 @@ func (c *Conn) exportedClose(code StatusCode, reason string) error {
 
 	// CloseErrors sent are made opaque to prevent applications from thinking
 	// they received a given status.
-	sentErr := xerrors.Errorf("sent close frame: %v", ce)
+	sentErr := fmt.Errorf("sent close frame: %v", ce)
 	err = c.writeClose(p, sentErr)
 	if err != nil {
 		return err
 	}
 
-	if !xerrors.Is(c.closeErr, sentErr) {
+	if !errors.Is(c.closeErr, sentErr) {
 		return c.closeErr
 	}
 
@@ -891,7 +890,7 @@ func (c *Conn) Ping(ctx context.Context) error {
 
 	err := c.ping(ctx, p)
 	if err != nil {
-		return xerrors.Errorf("failed to ping: %w", err)
+		return fmt.Errorf("failed to ping: %w", err)
 	}
 	return nil
 }
@@ -918,7 +917,7 @@ func (c *Conn) ping(ctx context.Context, p string) error {
 	case <-c.closed:
 		return c.closeErr
 	case <-ctx.Done():
-		err := xerrors.Errorf("failed to wait for pong: %w", ctx.Err())
+		err := fmt.Errorf("failed to wait for pong: %w", ctx.Err())
 		c.close(err)
 		return err
 	case <-pong:
diff --git a/websocket_test.go b/websocket_test.go
index ef6ae926878385da26d707386aa81e7188b8b778..1aa8b201a5a6a9d2f3dabb23207533cafdd9a1d7 100644
--- a/websocket_test.go
+++ b/websocket_test.go
@@ -5,6 +5,7 @@ import (
 	"context"
 	"encoding/binary"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -24,7 +25,6 @@ import (
 	"github.com/golang/protobuf/ptypes"
 	"github.com/golang/protobuf/ptypes/timestamp"
 	"go.uber.org/multierr"
-	"golang.org/x/xerrors"
 
 	"nhooyr.io/websocket"
 	"nhooyr.io/websocket/wsjson"
@@ -45,7 +45,7 @@ func TestHandshake(t *testing.T) {
 				c, err := websocket.Accept(w, r, nil)
 				if err == nil {
 					c.Close(websocket.StatusInternalError, "")
-					return xerrors.New("expected error regarding bad origin")
+					return errors.New("expected error regarding bad origin")
 				}
 				return assertErrorContains(err, "not authorized")
 			},
@@ -57,7 +57,7 @@ func TestHandshake(t *testing.T) {
 				})
 				if err == nil {
 					c.Close(websocket.StatusInternalError, "")
-					return xerrors.New("expected handshake failure")
+					return errors.New("expected handshake failure")
 				}
 				return assertErrorContains(err, "403")
 			},
@@ -115,7 +115,7 @@ func TestHandshake(t *testing.T) {
 			server: func(w http.ResponseWriter, r *http.Request) error {
 				cookie, err := r.Cookie("mycookie")
 				if err != nil {
-					return xerrors.Errorf("request is missing mycookie: %w", err)
+					return fmt.Errorf("request is missing mycookie: %w", err)
 				}
 				err = assertEqualf("myvalue", cookie.Value, "unexpected cookie value")
 				if err != nil {
@@ -131,11 +131,11 @@ func TestHandshake(t *testing.T) {
 			client: func(ctx context.Context, u string) error {
 				jar, err := cookiejar.New(nil)
 				if err != nil {
-					return xerrors.Errorf("failed to create cookie jar: %w", err)
+					return fmt.Errorf("failed to create cookie jar: %w", err)
 				}
 				parsedURL, err := url.Parse(u)
 				if err != nil {
-					return xerrors.Errorf("failed to parse url: %w", err)
+					return fmt.Errorf("failed to parse url: %w", err)
 				}
 				parsedURL.Scheme = "http"
 				jar.SetCookies(parsedURL, []*http.Cookie{
@@ -410,7 +410,7 @@ func TestConn(t *testing.T) {
 				case err = <-pingErrc:
 					return err
 				case <-ctx2.Done():
-					return xerrors.Errorf("failed to wait for pong: %w", ctx2.Err())
+					return fmt.Errorf("failed to wait for pong: %w", ctx2.Err())
 				}
 			},
 		},
@@ -576,15 +576,15 @@ func TestConn(t *testing.T) {
 				}
 				_, _, err = c.Read(ctx)
 				cerr := &websocket.CloseError{}
-				if !xerrors.As(err, cerr) || cerr.Code != websocket.StatusProtocolError {
-					return xerrors.Errorf("expected close error with StatusProtocolError: %+v", err)
+				if !errors.As(err, cerr) || cerr.Code != websocket.StatusProtocolError {
+					return fmt.Errorf("expected close error with StatusProtocolError: %+v", err)
 				}
 				return nil
 			},
 			client: func(ctx context.Context, c *websocket.Conn) error {
 				_, _, err := c.Read(ctx)
 				if err == nil || !strings.Contains(err.Error(), "rsv") {
-					return xerrors.Errorf("expected error that contains rsv: %+v", err)
+					return fmt.Errorf("expected error that contains rsv: %+v", err)
 				}
 				return nil
 			},
@@ -684,19 +684,19 @@ func TestConn(t *testing.T) {
 				}
 				_, err = w.Write([]byte(strings.Repeat("x", 10)))
 				if err != nil {
-					return xerrors.Errorf("expected non nil error")
+					return fmt.Errorf("expected non nil error")
 				}
 				err = c.Flush()
 				if err != nil {
-					return xerrors.Errorf("failed to flush: %w", err)
+					return fmt.Errorf("failed to flush: %w", err)
 				}
 				_, err = w.Write([]byte(strings.Repeat("x", 10)))
 				if err != nil {
-					return xerrors.Errorf("expected non nil error")
+					return fmt.Errorf("expected non nil error")
 				}
 				err = c.Flush()
 				if err != nil {
-					return xerrors.Errorf("failed to flush: %w", err)
+					return fmt.Errorf("failed to flush: %w", err)
 				}
 				_, _, err = c.Read(ctx)
 				return assertCloseStatus(err, websocket.StatusInternalError)
@@ -724,15 +724,15 @@ func TestConn(t *testing.T) {
 				}
 				_, err = w.Write([]byte(strings.Repeat("x", 10)))
 				if err != nil {
-					return xerrors.Errorf("expected non nil error")
+					return fmt.Errorf("expected non nil error")
 				}
 				err = c.Flush()
 				if err != nil {
-					return xerrors.Errorf("failed to flush: %w", err)
+					return fmt.Errorf("failed to flush: %w", err)
 				}
 				_, err = c.WriteFrame(ctx, true, websocket.OpBinary, []byte(strings.Repeat("x", 10)))
 				if err != nil {
-					return xerrors.Errorf("expected non nil error")
+					return fmt.Errorf("expected non nil error")
 				}
 				_, _, err = c.Read(ctx)
 				return assertErrorContains(err, "received new data message without finishing")
@@ -798,15 +798,15 @@ func TestConn(t *testing.T) {
 				}
 				_, err = w.Write([]byte(strings.Repeat("x", 10)))
 				if err != nil {
-					return xerrors.Errorf("expected non nil error")
+					return fmt.Errorf("expected non nil error")
 				}
 				err = c.Flush()
 				if err != nil {
-					return xerrors.Errorf("failed to flush: %w", err)
+					return fmt.Errorf("failed to flush: %w", err)
 				}
 				_, err = c.WriteFrame(ctx, true, websocket.OpBinary, []byte(strings.Repeat("x", 10)))
 				if err != nil {
-					return xerrors.Errorf("expected non nil error")
+					return fmt.Errorf("expected non nil error")
 				}
 				_, _, err = c.Read(ctx)
 				return assertCloseStatus(err, websocket.StatusProtocolError)
@@ -1890,8 +1890,8 @@ func echoLoop(ctx context.Context, c *websocket.Conn) {
 
 func assertCloseStatus(err error, code websocket.StatusCode) error {
 	var cerr websocket.CloseError
-	if !xerrors.As(err, &cerr) {
-		return xerrors.Errorf("no websocket close error in error chain: %+v", err)
+	if !errors.As(err, &cerr) {
+		return fmt.Errorf("no websocket close error in error chain: %+v", err)
 	}
 	return assertEqualf(code, cerr.Code, "unexpected status code")
 }
@@ -1949,7 +1949,7 @@ func assertSubprotocol(c *websocket.Conn, exp string) error {
 
 func assertEqualf(exp, act interface{}, f string, v ...interface{}) error {
 	if diff := cmpDiff(exp, act); diff != "" {
-		return xerrors.Errorf(f+": %v", append(v, diff)...)
+		return fmt.Errorf(f+": %v", append(v, diff)...)
 	}
 	return nil
 }
@@ -1965,14 +1965,14 @@ func assertNetConnRead(r io.Reader, exp string) error {
 
 func assertErrorContains(err error, exp string) error {
 	if err == nil || !strings.Contains(err.Error(), exp) {
-		return xerrors.Errorf("expected error that contains %q but got: %+v", exp, err)
+		return fmt.Errorf("expected error that contains %q but got: %+v", exp, err)
 	}
 	return nil
 }
 
 func assertErrorIs(exp, act error) error {
-	if !xerrors.Is(act, exp) {
-		return xerrors.Errorf("expected error %+v to be in %+v", exp, act)
+	if !errors.Is(act, exp) {
+		return fmt.Errorf("expected error %+v to be in %+v", exp, act)
 	}
 	return nil
 }
@@ -2000,7 +2000,7 @@ func assertReadCloseFrame(ctx context.Context, c *websocket.Conn, code websocket
 	}
 	ce, err := websocket.ParseClosePayload(actP)
 	if err != nil {
-		return xerrors.Errorf("failed to parse close frame payload: %w", err)
+		return fmt.Errorf("failed to parse close frame payload: %w", err)
 	}
 	return assertEqualf(ce.Code, code, "unexpected frame close frame code with payload %q", actP)
 }
diff --git a/wsjson/wsjson.go b/wsjson/wsjson.go
index fda429acb7a27b002e3b757bb6cfd439b6554bab..1e63f940ea7948c7267b36495e20c37e4e0f8588 100644
--- a/wsjson/wsjson.go
+++ b/wsjson/wsjson.go
@@ -4,8 +4,7 @@ package wsjson // import "nhooyr.io/websocket/wsjson"
 import (
 	"context"
 	"encoding/json"
-
-	"golang.org/x/xerrors"
+	"fmt"
 
 	"nhooyr.io/websocket"
 	"nhooyr.io/websocket/internal/bpool"
@@ -16,7 +15,7 @@ import (
 func Read(ctx context.Context, c *websocket.Conn, v interface{}) error {
 	err := read(ctx, c, v)
 	if err != nil {
-		return xerrors.Errorf("failed to read json: %w", err)
+		return fmt.Errorf("failed to read json: %w", err)
 	}
 	return nil
 }
@@ -29,7 +28,7 @@ func read(ctx context.Context, c *websocket.Conn, v interface{}) error {
 
 	if typ != websocket.MessageText {
 		c.Close(websocket.StatusUnsupportedData, "can only accept text messages")
-		return xerrors.Errorf("unexpected frame type for json (expected %v): %v", websocket.MessageText, typ)
+		return fmt.Errorf("unexpected frame type for json (expected %v): %v", websocket.MessageText, typ)
 	}
 
 	b := bpool.Get()
@@ -45,7 +44,7 @@ func read(ctx context.Context, c *websocket.Conn, v interface{}) error {
 	err = json.Unmarshal(b.Bytes(), v)
 	if err != nil {
 		c.Close(websocket.StatusInvalidFramePayloadData, "failed to unmarshal JSON")
-		return xerrors.Errorf("failed to unmarshal json: %w", err)
+		return fmt.Errorf("failed to unmarshal json: %w", err)
 	}
 
 	return nil
@@ -56,7 +55,7 @@ func read(ctx context.Context, c *websocket.Conn, v interface{}) error {
 func Write(ctx context.Context, c *websocket.Conn, v interface{}) error {
 	err := write(ctx, c, v)
 	if err != nil {
-		return xerrors.Errorf("failed to write json: %w", err)
+		return fmt.Errorf("failed to write json: %w", err)
 	}
 	return nil
 }
@@ -72,7 +71,7 @@ func write(ctx context.Context, c *websocket.Conn, v interface{}) error {
 	e := json.NewEncoder(w)
 	err = e.Encode(v)
 	if err != nil {
-		return xerrors.Errorf("failed to encode json: %w", err)
+		return fmt.Errorf("failed to encode json: %w", err)
 	}
 
 	err = w.Close()
diff --git a/wspb/wspb.go b/wspb/wspb.go
index dfc407cc3f3b17a2399f20e977c14646edfbcb37..8613a08093bdc2a68195f5cea55a1b84284a6a61 100644
--- a/wspb/wspb.go
+++ b/wspb/wspb.go
@@ -4,10 +4,10 @@ package wspb // import "nhooyr.io/websocket/wspb"
 import (
 	"bytes"
 	"context"
+	"fmt"
 	"sync"
 
 	"github.com/golang/protobuf/proto"
-	"golang.org/x/xerrors"
 
 	"nhooyr.io/websocket"
 	"nhooyr.io/websocket/internal/bpool"
@@ -18,7 +18,7 @@ import (
 func Read(ctx context.Context, c *websocket.Conn, v proto.Message) error {
 	err := read(ctx, c, v)
 	if err != nil {
-		return xerrors.Errorf("failed to read protobuf: %w", err)
+		return fmt.Errorf("failed to read protobuf: %w", err)
 	}
 	return nil
 }
@@ -31,7 +31,7 @@ func read(ctx context.Context, c *websocket.Conn, v proto.Message) error {
 
 	if typ != websocket.MessageBinary {
 		c.Close(websocket.StatusUnsupportedData, "can only accept binary messages")
-		return xerrors.Errorf("unexpected frame type for protobuf (expected %v): %v", websocket.MessageBinary, typ)
+		return fmt.Errorf("unexpected frame type for protobuf (expected %v): %v", websocket.MessageBinary, typ)
 	}
 
 	b := bpool.Get()
@@ -47,7 +47,7 @@ func read(ctx context.Context, c *websocket.Conn, v proto.Message) error {
 	err = proto.Unmarshal(b.Bytes(), v)
 	if err != nil {
 		c.Close(websocket.StatusInvalidFramePayloadData, "failed to unmarshal protobuf")
-		return xerrors.Errorf("failed to unmarshal protobuf: %w", err)
+		return fmt.Errorf("failed to unmarshal protobuf: %w", err)
 	}
 
 	return nil
@@ -58,7 +58,7 @@ func read(ctx context.Context, c *websocket.Conn, v proto.Message) error {
 func Write(ctx context.Context, c *websocket.Conn, v proto.Message) error {
 	err := write(ctx, c, v)
 	if err != nil {
-		return xerrors.Errorf("failed to write protobuf: %w", err)
+		return fmt.Errorf("failed to write protobuf: %w", err)
 	}
 	return nil
 }
@@ -74,7 +74,7 @@ func write(ctx context.Context, c *websocket.Conn, v proto.Message) error {
 
 	err := pb.Marshal(v)
 	if err != nil {
-		return xerrors.Errorf("failed to marshal protobuf: %w", err)
+		return fmt.Errorf("failed to marshal protobuf: %w", err)
 	}
 
 	return c.Write(ctx, websocket.MessageBinary, pb.Bytes())