Skip to content

Migrate from cockroachdb/errors

For most of a module this is an import-path change. The names and signatures match deliberately, so the compiler will find anything that is not.

The change

grep -rl '"github.com/cockroachdb/errors"' --include="*.go" . \
  | xargs sed -i 's|"github.com/cockroachdb/errors"|"gitlab.com/phpboyscout/go/errors"|'

go get gitlab.com/phpboyscout/go/errors
go mod tidy
go build ./... && go test ./...

That is the whole migration for a module that uses only the common surface. forge moved this way — 19 files, 84 references, no code changes.

What carries over unchanged

New · Newf · Errorf · Wrap · Wrapf · WithStack · Is · As · Unwrap · WithHint · WithHintf · WithDetail · GetAllHints · GetAllDetails · FlattenHints · FlattenDetails · Join

Three behaviour differences

Join gains a shape it did not have

cockroachdb's Join wraps its aggregate in a stack wrapper, so the value it returns does not satisfy Unwrap() []error. This one does, as the standard library does.

If you were reaching through it by hand, stop:

// before — the double unwrap cockroachdb needs
joined.(interface{ Unwrap() error }).Unwrap().(interface{ Unwrap() []error }).Unwrap()

// after
joined.(interface{ Unwrap() []error }).Unwrap()

More usefully: hints, details and attributes attached below a Join are now readable. They were not before. If you worked around that, the workaround can go.

Sentinels need NewSentinel

A package-level var Err… = errors.New(…) captures a stack at package initialisation — pointing at runtime.doInit, never at where the error was returned. Change those, and only those:

// before
var ErrNotFound = errors.New("not found")

// after
var ErrNotFound = errors.NewSentinel("mypkg.not_found", "not found")

Find them with:

grep -rnE '^\s*(var )?Err[A-Za-z]* += errors\.New' --include="*.go" .

Keep New everywhere else — inside a function it is doing the right thing. See Declare a sentinel for what the kind string is for.

auth.env-style ladders are gone, and so is Sentry

This package resolves nothing and reports to nothing. If you relied on cockroachdb's Sentry reporting, that belongs in go/observability.

What is not here

errorspb and the protobuf encoding, gRPC and HTTP status mapping, Sentry reporting, redaction interplay, domains, barriers, telemetry keys, context tags.

None had a call site in this estate. Wire serialization arrives later as a sibling module — the core is already shaped for it — and telemetry belongs to go/observability.

Sequence the estate leaf-first

Migrating one module does not reduce its dependency count, because cockroachdb/errors returns through any sibling that still uses it. Measured: forge alone went from 376 transitive packages to 377.

Migrate the leaves first — httpclient, grpcclient, transport-openapi, workspace, config-keychain, tls, which have 21 call sites between them — and the graph clears from the bottom up. httpclient is the one that matters: it is what puts cockroachdb/errors back into forge.

Expect the dependency count to stay flat until most of a graph has moved. That is the migration working, not failing.