Skip to content

Commit b33d88a

Browse files
committed
Expose and explain RegisterTypeMigration().
1 parent 102bafb commit b33d88a

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,28 @@ proposal](https://go.googlesource.com/proposal/+/master/design/29934-error-value
495495
printing out error details, and knows how to present a chain of
496496
causes in a semi-structured format upon formatting with `%+v`.
497497

498+
### Ensuring `errors.Is` works when packages are renamed
499+
500+
If a Go package containing a custom error type is renamed, or the
501+
error type itself is renamed, and errors of this type are transported
502+
over the network, then another system with a different code layout
503+
(e.g. running a different version of the software) may not be able to
504+
recognize the error any more via `errors.Is`.
505+
506+
To ensure that network portability continues to work across multiple
507+
software versions, in the case error types get renamed or Go packages
508+
get moved / renamed / etc, the server code must call
509+
`errors.RegisterTypeMigration()` from e.g. an `init()` function.
510+
511+
Example use:
512+
513+
```go
514+
previousPath := "github.com/old/path/to/error/package"
515+
previousTypeName := "oldpackage.oldErrorName"
516+
newErrorInstance := &newTypeName{...}
517+
errors.RegisterTypeMigration(previousPath, previousTypeName, newErrorInstance)
518+
```
519+
498520
## Error composition (summary)
499521

500522
| Constructor | Composes |
@@ -552,6 +574,9 @@ type LeafDecoder = func(ctx context.Context, msg string, safeDetails []string, p
552574
type WrapperEncoder = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message)
553575
type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, safeDetails []string, payload proto.Message) error
554576
577+
// Registering package renames for custom error types.
578+
func RegisterTypeMigration(previousPkgPath, previousTypeName string, newType error)
579+
555580
// Sentry reports.
556581
func BuildSentryReport(err error) (*sentry.Event, map[string]interface{})
557582
func ReportError(err error) (string)

errbase_api.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,27 @@ func FormatError(err error, s fmt.State, verb rune) { errbase.FormatError(err, s
177177
// will provide "smart" formatting even if the outer layer
178178
// of the error does not implement the Formatter interface.
179179
func Formattable(err error) fmt.Formatter { return errbase.Formattable(err) }
180+
181+
// RegisterTypeMigration tells the library that the type of the error
182+
// given as 3rd argument was previously known with type
183+
// previousTypeName, located at previousPkgPath.
184+
//
185+
// The value of previousTypeName must be the result of calling
186+
// reflect.TypeOf(err).String() on the original error object.
187+
// This is usually composed as follows:
188+
// [*]<shortpackage>.<errortype>
189+
//
190+
// For example, Go's standard error type has name "*errors.errorString".
191+
// The asterisk indicates that `errorString` implements the `error`
192+
// interface via pointer receiver.
193+
//
194+
// Meanwhile, the singleton error type context.DeadlineExceeded
195+
// has name "context.deadlineExceededError", without asterisk
196+
// because the type implements `error` by value.
197+
//
198+
// Remember that the short package name inside the error type name and
199+
// the last component of the package path can be different. This is
200+
// why they must be specified separately.
201+
func RegisterTypeMigration(previousPkgPath, previousTypeName string, newType error) {
202+
errbase.RegisterTypeMigration(previousPkgPath, previousTypeName, newType)
203+
}

0 commit comments

Comments
 (0)