replace errors.go with github.com/pkg/errors (2/2) (#3890)

* init of (2/2) common errors

* Remove instances of cmn.Error (2/2)

- Replace usage of cmnError and errorWrap
- ref #3862

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* comment wording

* simplify IsErrXXX functions

* log panic along with stopping the MConnection
This commit is contained in:
Marko 2019-08-11 19:03:40 +02:00 committed by Anton Kaliaev
parent 8dc39b69b7
commit 8a282a5fee
14 changed files with 75 additions and 88 deletions

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
@ -80,7 +81,7 @@ func runProxy(cmd *cobra.Command, args []string) error {
logger.Info("Constructing Verifier...") logger.Info("Constructing Verifier...")
cert, err := proxy.NewVerifier(chainID, home, node, logger, cacheSize) cert, err := proxy.NewVerifier(chainID, home, node, logger, cacheSize)
if err != nil { if err != nil {
return cmn.ErrorWrap(err, "constructing Verifier") return errors.Wrap(err, "constructing Verifier")
} }
cert.SetLogger(logger) cert.SetLogger(logger)
sc := proxy.SecureClient(node, cert) sc := proxy.SecureClient(node, cert)
@ -88,7 +89,7 @@ func runProxy(cmd *cobra.Command, args []string) error {
logger.Info("Starting proxy...") logger.Info("Starting proxy...")
err = proxy.StartProxy(sc, listenAddr, logger, maxOpenConnections) err = proxy.StartProxy(sc, listenAddr, logger, maxOpenConnections)
if err != nil { if err != nil {
return cmn.ErrorWrap(err, "starting proxy") return errors.Wrap(err, "starting proxy")
} }
// Run forever // Run forever

View File

@ -2,10 +2,9 @@ package merkle
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
cmn "github.com/tendermint/tendermint/libs/common" "github.com/pkg/errors"
) )
// SimpleProof represents a simple Merkle proof. // SimpleProof represents a simple Merkle proof.
@ -75,11 +74,11 @@ func (sp *SimpleProof) Verify(rootHash []byte, leaf []byte) error {
return errors.New("Proof index cannot be negative") return errors.New("Proof index cannot be negative")
} }
if !bytes.Equal(sp.LeafHash, leafHash) { if !bytes.Equal(sp.LeafHash, leafHash) {
return cmn.NewError("invalid leaf hash: wanted %X got %X", leafHash, sp.LeafHash) return errors.Errorf("invalid leaf hash: wanted %X got %X", leafHash, sp.LeafHash)
} }
computedHash := sp.ComputeRootHash() computedHash := sp.ComputeRootHash()
if !bytes.Equal(computedHash, rootHash) { if !bytes.Equal(computedHash, rootHash) {
return cmn.NewError("invalid root hash: wanted %X got %X", rootHash, computedHash) return errors.Errorf("invalid root hash: wanted %X got %X", rootHash, computedHash)
} }
return nil return nil
} }

View File

@ -3,6 +3,8 @@ package lite
import ( import (
"bytes" "bytes"
"github.com/pkg/errors"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
lerr "github.com/tendermint/tendermint/lite/errors" lerr "github.com/tendermint/tendermint/lite/errors"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
@ -63,7 +65,7 @@ func (bv *BaseVerifier) Verify(signedHeader types.SignedHeader) error {
// Do basic sanity checks. // Do basic sanity checks.
err := signedHeader.ValidateBasic(bv.chainID) err := signedHeader.ValidateBasic(bv.chainID)
if err != nil { if err != nil {
return cmn.ErrorWrap(err, "in verify") return errors.Wrap(err, "in verify")
} }
// Check commit signatures. // Check commit signatures.
@ -71,7 +73,7 @@ func (bv *BaseVerifier) Verify(signedHeader types.SignedHeader) error {
bv.chainID, signedHeader.Commit.BlockID, bv.chainID, signedHeader.Commit.BlockID,
signedHeader.Height, signedHeader.Commit) signedHeader.Height, signedHeader.Commit)
if err != nil { if err != nil {
return cmn.ErrorWrap(err, "in verify") return errors.Wrap(err, "in verify")
} }
return nil return nil

View File

@ -3,7 +3,7 @@ package errors
import ( import (
"fmt" "fmt"
cmn "github.com/tendermint/tendermint/libs/common" "github.com/pkg/errors"
) )
//---------------------------------------- //----------------------------------------
@ -49,15 +49,12 @@ func (e errEmptyTree) Error() string {
// ErrCommitNotFound indicates that a the requested commit was not found. // ErrCommitNotFound indicates that a the requested commit was not found.
func ErrCommitNotFound() error { func ErrCommitNotFound() error {
return cmn.ErrorWrap(errCommitNotFound{}, "") return errors.Wrap(errCommitNotFound{}, "")
} }
func IsErrCommitNotFound(err error) bool { func IsErrCommitNotFound(err error) bool {
if err_, ok := err.(cmn.Error); ok { _, ok := errors.Cause(err).(errCommitNotFound)
_, ok := err_.Data().(errCommitNotFound)
return ok return ok
}
return false
} }
//----------------- //-----------------
@ -65,18 +62,15 @@ func IsErrCommitNotFound(err error) bool {
// ErrUnexpectedValidators indicates a validator set mismatch. // ErrUnexpectedValidators indicates a validator set mismatch.
func ErrUnexpectedValidators(got, want []byte) error { func ErrUnexpectedValidators(got, want []byte) error {
return cmn.ErrorWrap(errUnexpectedValidators{ return errors.Wrap(errUnexpectedValidators{
got: got, got: got,
want: want, want: want,
}, "") }, "")
} }
func IsErrUnexpectedValidators(err error) bool { func IsErrUnexpectedValidators(err error) bool {
if err_, ok := err.(cmn.Error); ok { _, ok := errors.Cause(err).(errUnexpectedValidators)
_, ok := err_.Data().(errUnexpectedValidators)
return ok return ok
}
return false
} }
//----------------- //-----------------
@ -84,28 +78,22 @@ func IsErrUnexpectedValidators(err error) bool {
// ErrUnknownValidators indicates that some validator set was missing or unknown. // ErrUnknownValidators indicates that some validator set was missing or unknown.
func ErrUnknownValidators(chainID string, height int64) error { func ErrUnknownValidators(chainID string, height int64) error {
return cmn.ErrorWrap(errUnknownValidators{chainID, height}, "") return errors.Wrap(errUnknownValidators{chainID, height}, "")
} }
func IsErrUnknownValidators(err error) bool { func IsErrUnknownValidators(err error) bool {
if err_, ok := err.(cmn.Error); ok { _, ok := errors.Cause(err).(errUnknownValidators)
_, ok := err_.Data().(errUnknownValidators)
return ok return ok
}
return false
} }
//----------------- //-----------------
// ErrEmptyTree // ErrEmptyTree
func ErrEmptyTree() error { func ErrEmptyTree() error {
return cmn.ErrorWrap(errEmptyTree{}, "") return errors.Wrap(errEmptyTree{}, "")
} }
func IsErrEmptyTree(err error) bool { func IsErrEmptyTree(err error) bool {
if err_, ok := err.(cmn.Error); ok { _, ok := errors.Cause(err).(errEmptyTree)
_, ok := err_.Data().(errEmptyTree)
return ok return ok
}
return false
} }

View File

@ -1,7 +1,7 @@
package proxy package proxy
import ( import (
cmn "github.com/tendermint/tendermint/libs/common" "github.com/pkg/errors"
) )
type errNoData struct{} type errNoData struct{}
@ -12,13 +12,10 @@ func (e errNoData) Error() string {
// IsErrNoData checks whether an error is due to a query returning empty data // IsErrNoData checks whether an error is due to a query returning empty data
func IsErrNoData(err error) bool { func IsErrNoData(err error) bool {
if err_, ok := err.(cmn.Error); ok { _, ok := errors.Cause(err).(errNoData)
_, ok := err_.Data().(errNoData)
return ok return ok
}
return false
} }
func ErrNoData() error { func ErrNoData() error {
return cmn.ErrorWrap(errNoData{}, "") return errors.Wrap(errNoData{}, "")
} }

View File

@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"strings" "strings"
cmn "github.com/tendermint/tendermint/libs/common" "github.com/pkg/errors"
"github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/lite" "github.com/tendermint/tendermint/lite"
lerr "github.com/tendermint/tendermint/lite/errors" lerr "github.com/tendermint/tendermint/lite/errors"
rpcclient "github.com/tendermint/tendermint/rpc/client" rpcclient "github.com/tendermint/tendermint/rpc/client"
@ -83,7 +84,7 @@ func GetWithProofOptions(prt *merkle.ProofRuntime, path string, key []byte, opts
kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL) kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL)
err = prt.VerifyValue(resp.Proof, signedHeader.AppHash, kp.String(), resp.Value) err = prt.VerifyValue(resp.Proof, signedHeader.AppHash, kp.String(), resp.Value)
if err != nil { if err != nil {
return nil, cmn.ErrorWrap(err, "Couldn't verify value proof") return nil, errors.Wrap(err, "Couldn't verify value proof")
} }
return &ctypes.ResultABCIQuery{Response: resp}, nil return &ctypes.ResultABCIQuery{Response: resp}, nil
} else { } else {
@ -92,7 +93,7 @@ func GetWithProofOptions(prt *merkle.ProofRuntime, path string, key []byte, opts
// XXX How do we encode the key into a string... // XXX How do we encode the key into a string...
err = prt.VerifyAbsence(resp.Proof, signedHeader.AppHash, string(resp.Key)) err = prt.VerifyAbsence(resp.Proof, signedHeader.AppHash, string(resp.Key))
if err != nil { if err != nil {
return nil, cmn.ErrorWrap(err, "Couldn't verify absence proof") return nil, errors.Wrap(err, "Couldn't verify absence proof")
} }
return &ctypes.ResultABCIQuery{Response: resp}, nil return &ctypes.ResultABCIQuery{Response: resp}, nil
} }

View File

@ -1,7 +1,8 @@
package proxy package proxy
import ( import (
cmn "github.com/tendermint/tendermint/libs/common" "github.com/pkg/errors"
log "github.com/tendermint/tendermint/libs/log" log "github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/lite" "github.com/tendermint/tendermint/lite"
lclient "github.com/tendermint/tendermint/lite/client" lclient "github.com/tendermint/tendermint/lite/client"
@ -29,11 +30,11 @@ func NewVerifier(chainID, rootDir string, client lclient.SignStatusClient, logge
logger.Info("lite/proxy/NewVerifier found no trusted full commit, initializing from source from height 1...") logger.Info("lite/proxy/NewVerifier found no trusted full commit, initializing from source from height 1...")
fc, err := source.LatestFullCommit(chainID, 1, 1) fc, err := source.LatestFullCommit(chainID, 1, 1)
if err != nil { if err != nil {
return nil, cmn.ErrorWrap(err, "fetching source full commit @ height 1") return nil, errors.Wrap(err, "fetching source full commit @ height 1")
} }
err = trust.SaveFullCommit(fc) err = trust.SaveFullCommit(fc)
if err != nil { if err != nil {
return nil, cmn.ErrorWrap(err, "saving full commit to trusted") return nil, errors.Wrap(err, "saving full commit to trusted")
} }
} }

View File

@ -2,7 +2,8 @@ package conn
import ( import (
"bufio" "bufio"
"errors" "runtime/debug"
"fmt" "fmt"
"io" "io"
"math" "math"
@ -12,6 +13,8 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/pkg/errors"
amino "github.com/tendermint/go-amino" amino "github.com/tendermint/go-amino"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
flow "github.com/tendermint/tendermint/libs/flowrate" flow "github.com/tendermint/tendermint/libs/flowrate"
@ -313,8 +316,8 @@ func (c *MConnection) flush() {
// Catch panics, usually caused by remote disconnects. // Catch panics, usually caused by remote disconnects.
func (c *MConnection) _recover() { func (c *MConnection) _recover() {
if r := recover(); r != nil { if r := recover(); r != nil {
err := cmn.ErrorWrap(r, "recovered panic in MConnection") c.Logger.Error("MConnection panicked", "err", r, "stack", string(debug.Stack()))
c.stopForError(err) c.stopForError(errors.Errorf("recovered from panic: %v", r))
} }
} }

View File

@ -7,6 +7,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -113,13 +114,13 @@ func testOutboundPeerConn(
var pc peerConn var pc peerConn
conn, err := testDial(addr, config) conn, err := testDial(addr, config)
if err != nil { if err != nil {
return pc, cmn.ErrorWrap(err, "Error creating peer") return pc, errors.Wrap(err, "Error creating peer")
} }
pc, err = testPeerConn(conn, config, true, persistent, ourNodePrivKey, addr) pc, err = testPeerConn(conn, config, true, persistent, ourNodePrivKey, addr)
if err != nil { if err != nil {
if cerr := conn.Close(); cerr != nil { if cerr := conn.Close(); cerr != nil {
return pc, cmn.ErrorWrap(err, cerr.Error()) return pc, errors.Wrap(err, cerr.Error())
} }
return pc, err return pc, err
} }
@ -127,7 +128,7 @@ func testOutboundPeerConn(
// ensure dialed ID matches connection ID // ensure dialed ID matches connection ID
if addr.ID != pc.ID() { if addr.ID != pc.ID() {
if cerr := conn.Close(); cerr != nil { if cerr := conn.Close(); cerr != nil {
return pc, cmn.ErrorWrap(err, cerr.Error()) return pc, errors.Wrap(err, cerr.Error())
} }
return pc, ErrSwitchAuthenticationFailure{addr, pc.ID()} return pc, ErrSwitchAuthenticationFailure{addr, pc.ID()}
} }

View File

@ -5,11 +5,11 @@ import (
"testing" "testing"
"time" "time"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
) )
func getDialerTestCases(t *testing.T) []dialerTestCase { func getDialerTestCases(t *testing.T) []dialerTestCase {
@ -44,6 +44,6 @@ func TestIsConnTimeoutForWrappedConnTimeouts(t *testing.T) {
dialer := DialTCPFn(tcpAddr, time.Millisecond, ed25519.GenPrivKey()) dialer := DialTCPFn(tcpAddr, time.Millisecond, ed25519.GenPrivKey())
_, err := dialer() _, err := dialer()
assert.Error(t, err) assert.Error(t, err)
err = cmn.ErrorWrap(ErrConnectionTimeout, err.Error()) err = errors.Wrap(ErrConnectionTimeout, err.Error())
assert.True(t, IsConnTimeout(err)) assert.True(t, IsConnTimeout(err))
} }

View File

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"net" "net"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
@ -13,15 +15,14 @@ import (
// report that a connection timeout occurred. This detects both fundamental // report that a connection timeout occurred. This detects both fundamental
// network timeouts, as well as ErrConnTimeout errors. // network timeouts, as well as ErrConnTimeout errors.
func IsConnTimeout(err error) bool { func IsConnTimeout(err error) bool {
if cmnErr, ok := err.(cmn.Error); ok { switch errors.Cause(err).(type) {
if cmnErr.Data() == ErrConnectionTimeout { case EndpointTimeoutError:
return true return true
} case timeoutError:
}
if _, ok := err.(timeoutError); ok {
return true return true
} default:
return false return false
}
} }
// NewSignerListener creates a new SignerListenerEndpoint using the corresponding listen address // NewSignerListener creates a new SignerListenerEndpoint using the corresponding listen address

View File

@ -1,15 +1,13 @@
package privval package privval
import ( import (
"fmt"
"testing" "testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
cmn "github.com/tendermint/tendermint/libs/common"
) )
func TestIsConnTimeoutForNonTimeoutErrors(t *testing.T) { func TestIsConnTimeoutForNonTimeoutErrors(t *testing.T) {
assert.False(t, IsConnTimeout(cmn.ErrorWrap(ErrDialRetryMax, "max retries exceeded"))) assert.False(t, IsConnTimeout(errors.Wrap(ErrDialRetryMax, "max retries exceeded")))
assert.False(t, IsConnTimeout(fmt.Errorf("completely irrelevant error"))) assert.False(t, IsConnTimeout(errors.New("completely irrelevant error")))
} }

View File

@ -1,6 +1,8 @@
package types package types
import ( import (
"github.com/pkg/errors"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
@ -95,38 +97,38 @@ func (params *ValidatorParams) IsValidPubkeyType(pubkeyType string) bool {
// allowed limits, and returns an error if they are not. // allowed limits, and returns an error if they are not.
func (params *ConsensusParams) Validate() error { func (params *ConsensusParams) Validate() error {
if params.Block.MaxBytes <= 0 { if params.Block.MaxBytes <= 0 {
return cmn.NewError("Block.MaxBytes must be greater than 0. Got %d", return errors.Errorf("Block.MaxBytes must be greater than 0. Got %d",
params.Block.MaxBytes) params.Block.MaxBytes)
} }
if params.Block.MaxBytes > MaxBlockSizeBytes { if params.Block.MaxBytes > MaxBlockSizeBytes {
return cmn.NewError("Block.MaxBytes is too big. %d > %d", return errors.Errorf("Block.MaxBytes is too big. %d > %d",
params.Block.MaxBytes, MaxBlockSizeBytes) params.Block.MaxBytes, MaxBlockSizeBytes)
} }
if params.Block.MaxGas < -1 { if params.Block.MaxGas < -1 {
return cmn.NewError("Block.MaxGas must be greater or equal to -1. Got %d", return errors.Errorf("Block.MaxGas must be greater or equal to -1. Got %d",
params.Block.MaxGas) params.Block.MaxGas)
} }
if params.Block.TimeIotaMs <= 0 { if params.Block.TimeIotaMs <= 0 {
return cmn.NewError("Block.TimeIotaMs must be greater than 0. Got %v", return errors.Errorf("Block.TimeIotaMs must be greater than 0. Got %v",
params.Block.TimeIotaMs) params.Block.TimeIotaMs)
} }
if params.Evidence.MaxAge <= 0 { if params.Evidence.MaxAge <= 0 {
return cmn.NewError("EvidenceParams.MaxAge must be greater than 0. Got %d", return errors.Errorf("EvidenceParams.MaxAge must be greater than 0. Got %d",
params.Evidence.MaxAge) params.Evidence.MaxAge)
} }
if len(params.Validator.PubKeyTypes) == 0 { if len(params.Validator.PubKeyTypes) == 0 {
return cmn.NewError("len(Validator.PubKeyTypes) must be greater than 0") return errors.New("len(Validator.PubKeyTypes) must be greater than 0")
} }
// Check if keyType is a known ABCIPubKeyType // Check if keyType is a known ABCIPubKeyType
for i := 0; i < len(params.Validator.PubKeyTypes); i++ { for i := 0; i < len(params.Validator.PubKeyTypes); i++ {
keyType := params.Validator.PubKeyTypes[i] keyType := params.Validator.PubKeyTypes[i]
if _, ok := ABCIPubKeyTypesToAminoNames[keyType]; !ok { if _, ok := ABCIPubKeyTypesToAminoNames[keyType]; !ok {
return cmn.NewError("params.Validator.PubKeyTypes[%d], %s, is an unknown pubkey type", return errors.Errorf("params.Validator.PubKeyTypes[%d], %s, is an unknown pubkey type",
i, keyType) i, keyType)
} }
} }

View File

@ -2,15 +2,15 @@ package types
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
"sort" "sort"
"strings" "strings"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"
) )
// MaxTotalVotingPower - the maximum allowed total voting power. // MaxTotalVotingPower - the maximum allowed total voting power.
@ -681,13 +681,13 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin
continue continue
} }
if precommit.Height != height { if precommit.Height != height {
return cmn.NewError("Blocks don't match - %d vs %d", round, precommit.Round) return errors.Errorf("Blocks don't match - %d vs %d", round, precommit.Round)
} }
if precommit.Round != round { if precommit.Round != round {
return cmn.NewError("Invalid commit -- wrong round: %v vs %v", round, precommit.Round) return errors.Errorf("Invalid commit -- wrong round: %v vs %v", round, precommit.Round)
} }
if precommit.Type != PrecommitType { if precommit.Type != PrecommitType {
return cmn.NewError("Invalid commit -- not precommit @ index %v", idx) return errors.Errorf("Invalid commit -- not precommit @ index %v", idx)
} }
// See if this validator is in oldVals. // See if this validator is in oldVals.
oldIdx, val := oldVals.GetByAddress(precommit.ValidatorAddress) oldIdx, val := oldVals.GetByAddress(precommit.ValidatorAddress)
@ -699,7 +699,7 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin
// Validate signature. // Validate signature.
precommitSignBytes := commit.VoteSignBytes(chainID, idx) precommitSignBytes := commit.VoteSignBytes(chainID, idx)
if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
return cmn.NewError("Invalid commit -- invalid signature: %v", precommit) return errors.Errorf("Invalid commit -- invalid signature: %v", precommit)
} }
// Good precommit! // Good precommit!
if blockID.Equals(precommit.BlockID) { if blockID.Equals(precommit.BlockID) {
@ -721,15 +721,8 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin
// ErrTooMuchChange // ErrTooMuchChange
func IsErrTooMuchChange(err error) bool { func IsErrTooMuchChange(err error) bool {
switch err_ := err.(type) { _, ok := errors.Cause(err).(errTooMuchChange)
case cmn.Error:
_, ok := err_.Data().(errTooMuchChange)
return ok return ok
case errTooMuchChange:
return true
default:
return false
}
} }
type errTooMuchChange struct { type errTooMuchChange struct {