Repository: coinbase/rosetta-cli
Pull request #353 (2022-09-13 18:57:44)

Error handling improvement for processor, tester and error packages
Signed-off-by: Jingfu Wang <jingfu.wang@coinbase.com>

Error handling improvement for processor, tester and error packages

### Motivation
<!--
Does this solve a bug? Enable a new use-case? Improve an existing behavior? Concrete examples are helpful here.
-->

### Solution
<!--
What is the solution here from a high level. What are the key technical decisions and why were they made?
-->

### Open questions
<!--
(optional) Any open questions or feedback on design desired?
-->



Patch:
diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go
index 02e34a5..5fa4293 100644
--- a/pkg/errors/errors.go
+++ b/pkg/errors/errors.go
@@ -39,8 +39,12 @@ var (
 	ErrConstructionConfigMissing                                         = errors.New("construction configuration is missing")
 
 	// Data check errors
-	ErrDataCheckHalt         = errors.New("data check halted")
-	ErrReconciliationFailure = errors.New("reconciliation failure")
+	ErrDataCheckHalt          = errors.New("data check halted")
+	ErrReconciliationFailure  = errors.New("reconciliation failure")
+	ErrInitDataTester         = errors.New("unexpected error occurred while trying to initialize data tester")
+	ErrReconcilerDrainHalt    = errors.New("reconciler queue drain halted")
+	ErrMissingOps             = errors.New("search for block with missing ops halted")
+	ErrUnableToFindMissingOps = errors.New("unable to find missing ops")
 
 	// Spec check errors
 	ErrErrorEmptyMessage  = errors.New("error object can't have empty message")
@@ -50,18 +54,23 @@ var (
 	ErrBlockTip           = errors.New("unspecified block_identifier doesn't give the tip block")
 
 	// Construction check errors
-	ErrConstructionCheckHalt = errors.New("construction check halted")
+	ErrConstructionCheckHalt                            = errors.New("construction check halted")
+	ErrBalanceExemptionsWithInitialBalanceFetchDisabled = errors.New("found balance exemptions but initial balance fetch disabled")
 
 	// Command errors
-	ErrBlockNotFound                 = errors.New("block not found")
-	ErrNoAvailableNetwork            = errors.New("no networks available")
-	ErrNetworkOptionsAllowlistIsNil  = errors.New("network options allowlist is nil")
-	ErrAsserterConfigurationIsNil    = errors.New("asserter configuration is nil")
-	ErrTimestampStartIndexMismatch   = errors.New("timestamp start index mismatch")
-	ErrOperationTypeLengthMismatch   = errors.New("operation type length mismatch")
-	ErrOperationTypeMismatch         = errors.New("operation type mismatch")
-	ErrOperationStatusLengthMismatch = errors.New("operation status length mismatch")
-	ErrOperationStatusMismatch       = errors.New("operation status mismatch")
-	ErrErrorLengthMismatch           = errors.New("error length mismatch")
-	ErrErrorMismatch                 = errors.New("error mismatch")
+	ErrBlockNotFound                  = errors.New("block not found")
+	ErrNoAvailableNetwork             = errors.New("no networks available")
+	ErrNetworkOptionsAllowlistIsNil   = errors.New("network options allowlist is nil")
+	ErrAsserterConfigurationIsNil     = errors.New("asserter configuration is nil")
+	ErrTimestampStartIndexMismatch    = errors.New("timestamp start index mismatch")
+	ErrOperationTypeLengthMismatch    = errors.New("operation type length mismatch")
+	ErrOperationTypeMismatch          = errors.New("operation type mismatch")
+	ErrOperationStatusLengthMismatch  = errors.New("operation status length mismatch")
+	ErrOperationStatusMismatch        = errors.New("operation status mismatch")
+	ErrErrorLengthMismatch            = errors.New("error length mismatch")
+	ErrErrorMismatch                  = errors.New("error mismatch")
+	ErrAsserterConfigError            = errors.New("asserter configuration validation failed")
+	ErrNoHeadBlock                    = errors.New("no head block")
+	ErrBlockBenchmarkTimeout          = errors.New("/block endpoint benchmarking timed out")
+	ErrAccountBalanceBenchmarkTimeout = errors.New("/account/balance endpoint benchmarking timed out")
 )
diff --git a/pkg/processor/balance_storage_handler.go b/pkg/processor/balance_storage_handler.go
index 84e0df1..9dd251e 100644
--- a/pkg/processor/balance_storage_handler.go
+++ b/pkg/processor/balance_storage_handler.go
@@ -16,6 +16,7 @@ package processor
 
 import (
 	"context"
+	"fmt"
 	"math/big"
 
 	"github.com/coinbase/rosetta-cli/pkg/logger"
@@ -124,7 +125,7 @@ func (h *BalanceStorageHandler) AccountsReconciled(
 		modules.ReconciledAccounts,
 		big.NewInt(int64(count)),
 	)
-	return err
+	return fmt.Errorf("failed to update the total accounts reconciled by count: %w", err)
 }
 
 // AccountsSeen updates the total accounts seen by count.
@@ -139,5 +140,5 @@ func (h *BalanceStorageHandler) AccountsSeen(
 		modules.SeenAccounts,
 		big.NewInt(int64(count)),
 	)
-	return err
+	return fmt.Errorf("failed to update the total accounts seen by count: %w", err)
 }
diff --git a/pkg/processor/balance_storage_helper.go b/pkg/processor/balance_storage_helper.go
index f862093..f043691 100644
--- a/pkg/processor/balance_storage_helper.go
+++ b/pkg/processor/balance_storage_helper.go
@@ -110,7 +110,7 @@ func (h *BalanceStorageHelper) AccountBalance(
 		lookupBlock.Index,
 	)
 	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get currency balance", err)
+		return nil, fmt.Errorf("unable to get balance of currency %s for account %s: %w", types.PrintStruct(currency), types.PrintStruct(account), err)
 	}
 
 	// If the returned balance block does not match the intended
diff --git a/pkg/processor/broadcast_storage_handler.go b/pkg/processor/broadcast_storage_handler.go
index edb57c1..aad3449 100644
--- a/pkg/processor/broadcast_storage_handler.go
+++ b/pkg/processor/broadcast_storage_handler.go
@@ -72,7 +72,7 @@ func (h *BroadcastStorageHandler) TransactionConfirmed(
 ) error {
 	_, _, relatedTransactions, err := h.blockStorage.FindRelatedTransactions(ctx, transaction.TransactionIdentifier, dbTx)
 	if err != nil {
-		return fmt.Errorf("%w: could not find related transactions", err)
+		return fmt.Errorf("failed to find related transactions %s: %w", types.PrintStruct(transaction.TransactionIdentifier), err)
 	}
 
 	observed := transaction.Operations
@@ -81,7 +81,7 @@ func (h *BroadcastStorageHandler) TransactionConfirmed(
 	}
 
 	if err := h.parser.ExpectedOperations(intent, observed, false, true); err != nil {
-		return fmt.Errorf("%w: confirmed transaction did not match intent", err)
+		return fmt.Errorf("confirmed transaction did not match intent: %w", err)
 	}
 
 	// Validate destination memo if it's needed
@@ -108,7 +108,7 @@ func (h *BroadcastStorageHandler) TransactionConfirmed(
 		identifier,
 		transaction,
 	); err != nil {
-		return fmt.Errorf("%w: coordinator could not handle transaction", err)
+		return fmt.Errorf("coordinator could not handle transaction: %w", err)
 	}
 
 	return nil
@@ -155,7 +155,7 @@ func (h *BroadcastStorageHandler) BroadcastFailed(
 		identifier,
 		nil,
 	); err != nil {
-		return fmt.Errorf("%w: coordinator could not handle transaction", err)
+		return fmt.Errorf("coordinator could not handle transaction: %w", err)
 	}
 
 	if h.config.Construction.IgnoreBroadcastFailures {
diff --git a/pkg/processor/broadcast_storage_helper.go b/pkg/processor/broadcast_storage_helper.go
index 5e88185..c509559 100644
--- a/pkg/processor/broadcast_storage_helper.go
+++ b/pkg/processor/broadcast_storage_helper.go
@@ -17,6 +17,7 @@ package processor
 import (
 	"context"
 	"fmt"
+
 	"github.com/coinbase/rosetta-sdk-go/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
@@ -55,7 +56,7 @@ func (h *BroadcastStorageHelper) AtTip(
 ) (bool, error) {
 	atTip, _, err := utils.CheckStorageTip(ctx, h.network, tipDelay, h.fetcher, h.blockStorage)
 	if err != nil {
-		return false, err
+		return false, fmt.Errorf("failed to check storage tip: %w", err)
 	}
 
 	return atTip, nil
@@ -68,7 +69,7 @@ func (h *BroadcastStorageHelper) CurrentBlockIdentifier(
 ) (*types.BlockIdentifier, error) {
 	blockIdentifier, err := h.blockStorage.GetHeadBlockIdentifier(ctx)
 	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get head block identifier", err)
+		return nil, fmt.Errorf("unable to get head block identifier: %w", err)
 	}
 
 	return blockIdentifier, nil
@@ -84,7 +85,7 @@ func (h *BroadcastStorageHelper) FindTransaction(
 ) (*types.BlockIdentifier, *types.Transaction, error) {
 	newestBlock, transaction, err := h.blockStorage.FindTransaction(ctx, transactionIdentifier, txn)
 	if err != nil {
-		return nil, nil, fmt.Errorf("%w: unable to perform transaction search", err)
+		return nil, nil, fmt.Errorf("unable to perform transaction search for transaction %s: %w", types.PrintStruct(transactionIdentifier), err)
 	}
 
 	return newestBlock, transaction, nil
@@ -103,7 +104,7 @@ func (h *BroadcastStorageHelper) BroadcastTransaction(
 		networkTransaction,
 	)
 	if fetchErr != nil {
-		return nil, fmt.Errorf("%w: unable to broadcast transaction", fetchErr.Err)
+		return nil, fmt.Errorf("unable to broadcast transaction %s: %w", networkTransaction, fetchErr.Err)
 	}
 
 	return transactionIdentifier, nil
diff --git a/pkg/processor/coordinator_helper.go b/pkg/processor/coordinator_helper.go
index 496611b..f21d91a 100644
--- a/pkg/processor/coordinator_helper.go
+++ b/pkg/processor/coordinator_helper.go
@@ -16,11 +16,11 @@ package processor
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"log"
 	"math/big"
 
+	cliErrs "github.com/coinbase/rosetta-cli/pkg/errors"
 	"github.com/coinbase/rosetta-sdk-go/constructor/coordinator"
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/keys"
@@ -153,7 +153,7 @@ func (c *CoordinatorHelper) Derive(
 	)
 	if fetchErr != nil {
 		c.verboseLog(reqerror, constructionDerive, arg{argError, fetchErr})
-		return nil, nil, fetchErr.Err
+		return nil, nil, fmt.Errorf("/construction/derive call is failed: %w", fetchErr.Err)
 	}
 
 	c.verboseLog(response, constructionDerive,
@@ -185,7 +185,7 @@ func (c *CoordinatorHelper) Preprocess(
 
 	if fetchErr != nil {
 		c.verboseLog(reqerror, constructionPreprocess, arg{argError, fetchErr})
-		return nil, nil, fetchErr.Err
+		return nil, nil, fmt.Errorf("/construction/preprocess call is failed: %w", fetchErr.Err)
 	}
 
 	c.verboseLog(response, constructionPreprocess,
@@ -217,7 +217,7 @@ func (c *CoordinatorHelper) Metadata(
 
 	if fetchErr != nil {
 		c.verboseLog(reqerror, constructionMetadata, arg{argError, fetchErr})
-		return nil, nil, fetchErr.Err
+		return nil, nil, fmt.Errorf("/construction/metadata call is failed: %w", fetchErr.Err)
 	}
 
 	c.verboseLog(response, constructionMetadata,
@@ -251,7 +251,7 @@ func (c *CoordinatorHelper) Payloads(
 
 	if fetchErr != nil {
 		c.verboseLog(reqerror, constructionPayloads, arg{argError, fetchErr})
-		return "", nil, fetchErr.Err
+		return "", nil, fmt.Errorf("/construction/payloads call is failed: %w", fetchErr.Err)
 	}
 
 	c.verboseLog(response, constructionPayloads,
@@ -283,7 +283,7 @@ func (c *CoordinatorHelper) Parse(
 
 	if fetchErr != nil {
 		c.verboseLog(reqerror, constructionParse, arg{argError, fetchErr})
-		return nil, nil, nil, fetchErr.Err
+		return nil, nil, nil, fmt.Errorf("/construction/parse call is failed: %w", fetchErr.Err)
 	}
 
 	c.verboseLog(response, constructionParse,
@@ -316,7 +316,7 @@ func (c *CoordinatorHelper) Combine(
 
 	if fetchErr != nil {
 		c.verboseLog(reqerror, constructionCombine, arg{argError, fetchErr})
-		return "", fetchErr.Err
+		return "", fmt.Errorf("/construction/combine call is failed: %w", fetchErr.Err)
 	}
 
 	c.verboseLog(response, constructionCombine, arg{argNetworkTransaction, res})
@@ -342,7 +342,7 @@ func (c *CoordinatorHelper) Hash(
 
 	if fetchErr != nil {
 		c.verboseLog(reqerror, constructionHash, arg{argError, fetchErr})
-		return nil, fetchErr.Err
+		return nil, fmt.Errorf("/construction/hash call is failed: %w", fetchErr.Err)
 	}
 
 	c.verboseLog(response, constructionHash, arg{argTransactionIdentifier, res})
@@ -400,10 +400,10 @@ func (c *CoordinatorHelper) Balance(
 ) (*types.Amount, error) {
 	headBlock, err := c.blockStorage.GetHeadBlockIdentifier(ctx)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("failed to get head block identifier: %w", err)
 	}
 	if headBlock == nil {
-		return nil, errors.New("no blocks synced")
+		return nil, cliErrs.ErrNoHeadBlock
 	}
 
 	return c.balanceStorage.GetOrSetBalanceTransactional(
@@ -429,7 +429,7 @@ func (c *CoordinatorHelper) Coins(
 		accountIdentifier,
 	)
 	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get coins", err)
+		return nil, fmt.Errorf("unable to get coins for account %s: %w", types.PrintStruct(accountIdentifier), err)
 	}
 
 	coinsToReturn := []*types.Coin{}
diff --git a/pkg/processor/reconciler_handler.go b/pkg/processor/reconciler_handler.go
index f7148b7..b6a6154 100644
--- a/pkg/processor/reconciler_handler.go
+++ b/pkg/processor/reconciler_handler.go
@@ -112,7 +112,7 @@ func (h *ReconcilerHandler) UpdateCounts(ctx context.Context) error {
 		}
 
 		if _, err := h.counterStorage.Update(ctx, key, big.NewInt(count)); err != nil {
-			return err
+			return fmt.Errorf("failed to key %s in counter storage: %w", key, err)
 		}
 	}
 
@@ -145,7 +145,7 @@ func (h *ReconcilerHandler) ReconciliationFailed(
 		block,
 	)
 	if err != nil {
-		return err
+		return fmt.Errorf("failed to log reconciliation checks when reconciliation is failed: %w", err)
 	}
 
 	if h.haltOnReconciliationError {
@@ -161,28 +161,28 @@ func (h *ReconcilerHandler) ReconciliationFailed(
 			}
 			h.InactiveFailureBlock = block
 			return fmt.Errorf(
-				"%w: inactive reconciliation error for %s at %d (computed: %s%s, live: %s%s)",
-				cliErrs.ErrReconciliationFailure,
+				"inactive reconciliation error for account address %s at block index %d (computed: %s%s, live: %s%s): %w",
 				account.Address,
 				block.Index,
 				computedBalance,
 				currency.Symbol,
 				liveBalance,
 				currency.Symbol,
+				cliErrs.ErrReconciliationFailure,
 			)
 		}
 
 		// If we halt on an active reconciliation error, store in the handler.
 		h.ActiveFailureBlock = block
 		return fmt.Errorf(
-			"%w: active reconciliation error for %s at %d (computed: %s%s, live: %s%s)",
-			cliErrs.ErrReconciliationFailure,
+			"active reconciliation error for account address %s at block index %d (computed: %s%s, live: %s%s): %w",
 			account.Address,
 			block.Index,
 			computedBalance,
 			currency.Symbol,
 			liveBalance,
 			currency.Symbol,
+			cliErrs.ErrReconciliationFailure,
 		)
 	}
 
@@ -209,7 +209,7 @@ func (h *ReconcilerHandler) ReconciliationExempt(
 	// we still mark the account as being reconciled because the balance was in the range
 	// specified by exemption.
 	if err := h.balanceStorage.Reconciled(ctx, account, currency, block); err != nil {
-		return fmt.Errorf("%w: unable to store updated reconciliation", err)
+		return fmt.Errorf("unable to store updated reconciliation currency %s of account %s at block %s: %w", types.PrintStruct(currency), types.PrintStruct(account), types.PrintStruct(block), err)
 	}
 
 	return nil
@@ -250,7 +250,7 @@ func (h *ReconcilerHandler) ReconciliationSucceeded(
 	h.counterLock.Unlock()
 
 	if err := h.balanceStorage.Reconciled(ctx, account, currency, block); err != nil {
-		return fmt.Errorf("%w: unable to store updated reconciliation", err)
+		return fmt.Errorf("unable to store updated reconciliation currency %s of account %s at block %s: %w", types.PrintStruct(currency), types.PrintStruct(account), types.PrintStruct(block), err)
 	}
 
 	return h.logger.ReconcileSuccessStream(
diff --git a/pkg/processor/reconciler_helper.go b/pkg/processor/reconciler_helper.go
index 3fb163f..dd50c76 100644
--- a/pkg/processor/reconciler_helper.go
+++ b/pkg/processor/reconciler_helper.go
@@ -16,6 +16,7 @@ package processor
 
 import (
 	"context"
+	"fmt"
 
 	"github.com/coinbase/rosetta-cli/configuration"
 
@@ -138,7 +139,7 @@ func (h *ReconcilerHelper) LiveBalance(
 		index,
 	)
 	if err != nil {
-		return nil, nil, err
+		return nil, nil, fmt.Errorf("failed to get current balance of currency %s of account %s: %w", types.PrintStruct(currency), types.PrintStruct(account), err)
 	}
 	return amt, block, nil
 }
diff --git a/pkg/results/construction_results.go b/pkg/results/construction_results.go
index e578b26..3d96824 100644
--- a/pkg/results/construction_results.go
+++ b/pkg/results/construction_results.go
@@ -65,7 +65,7 @@ func (c *CheckConstructionResults) Output(path string) {
 	if len(path) > 0 {
 		writeErr := utils.SerializeAndWrite(path, c)
 		if writeErr != nil {
-			log.Printf("%s: unable to save results\n", writeErr.Error())
+			log.Printf("unable to save results: %s\n", writeErr.Error())
 		}
 	}
 }
diff --git a/pkg/results/data_results.go b/pkg/results/data_results.go
index 6ba9251..7ff08df 100644
--- a/pkg/results/data_results.go
+++ b/pkg/results/data_results.go
@@ -92,7 +92,7 @@ func (c *CheckDataResults) Output(path string) {
 	if len(path) > 0 {
 		writeErr := utils.SerializeAndWrite(path, c)
 		if writeErr != nil {
-			log.Printf("%s: unable to save results\n", writeErr.Error())
+			log.Printf("unable to save results: %s\n", writeErr.Error())
 		}
 	}
 }
@@ -192,61 +192,61 @@ func ComputeCheckDataStats(
 
 	blocks, err := counters.Get(ctx, modules.BlockCounter)
 	if err != nil {
-		log.Printf("%s: cannot get block counter", err.Error())
+		log.Printf("cannot get block counter: %s", err.Error())
 		return nil
 	}
 
 	orphans, err := counters.Get(ctx, modules.OrphanCounter)
 	if err != nil {
-		log.Printf("%s: cannot get orphan counter", err.Error())
+		log.Printf("cannot get orphan counter: %s", err.Error())
 		return nil
 	}
 
 	txs, err := counters.Get(ctx, modules.TransactionCounter)
 	if err != nil {
-		log.Printf("%s: cannot get transaction counter", err.Error())
+		log.Printf("cannot get transaction counter: %s", err.Error())
 		return nil
 	}
 
 	ops, err := counters.Get(ctx, modules.OperationCounter)
 	if err != nil {
-		log.Printf("%s: cannot get operations counter", err.Error())
+		log.Printf("cannot get operations counter: %s", err.Error())
 		return nil
 	}
 
 	accounts, err := counters.Get(ctx, modules.SeenAccounts)
 	if err != nil {
-		log.Printf("%s: cannot get accounts counter", err.Error())
+		log.Printf("cannot get accounts counter: %s", err.Error())
 		return nil
 	}
 
 	activeReconciliations, err := counters.Get(ctx, modules.ActiveReconciliationCounter)
 	if err != nil {
-		log.Printf("%s: cannot get active reconciliations counter", err.Error())
+		log.Printf("cannot get active reconciliations counter: %s", err.Error())
 		return nil
 	}
 
 	inactiveReconciliations, err := counters.Get(ctx, modules.InactiveReconciliationCounter)
 	if err != nil {
-		log.Printf("%s: cannot get inactive reconciliations counter", err.Error())
+		log.Printf("cannot get inactive reconciliations counter: %s", err.Error())
 		return nil
 	}
 
 	exemptReconciliations, err := counters.Get(ctx, modules.ExemptReconciliationCounter)
 	if err != nil {
-		log.Printf("%s: cannot get exempt reconciliations counter", err.Error())
+		log.Printf("cannot get exempt reconciliations counter: %s", err.Error())
 		return nil
 	}
 
 	failedReconciliations, err := counters.Get(ctx, modules.FailedReconciliationCounter)
 	if err != nil {
-		log.Printf("%s: cannot get failed reconciliations counter", err.Error())
+		log.Printf("cannot get failed reconciliations counter: %s", err.Error())
 		return nil
 	}
 
 	skippedReconciliations, err := counters.Get(ctx, modules.SkippedReconciliationsCounter)
 	if err != nil {
-		log.Printf("%s: cannot get skipped reconciliations counter", err.Error())
+		log.Printf("cannot get skipped reconciliations counter: %s", err.Error())
 		return nil
 	}
 
@@ -272,7 +272,7 @@ func ComputeCheckDataStats(
 			// In this case, we use the default 0 value for the reconciliation
 			// coverage in stats.
 		case err != nil:
-			log.Printf("%s: cannot get reconciliation coverage", err.Error())
+			log.Printf("cannot get reconciliation coverage: %s", err.Error())
 			return nil
 		}
 	}
@@ -304,7 +304,7 @@ func ComputeCheckDataProgress(
 ) *CheckDataProgress {
 	networkStatus, fetchErr := fetcher.NetworkStatusRetry(ctx, network, nil)
 	if fetchErr != nil {
-		fmt.Printf("%s: cannot get network status", fetchErr.Err.Error())
+		fmt.Printf("cannot get network status: %s", fetchErr.Err.Error())
 		return nil
 	}
 	tipIndex := networkStatus.CurrentBlockIdentifier.Index
@@ -315,19 +315,19 @@ func ComputeCheckDataProgress(
 	}
 
 	// Get current tip in the case that re-orgs occurred
-	// or a custom start index was provied.
+	// or a custom start index was provided.
 	headBlock, err := blockStorage.GetHeadBlockIdentifier(ctx)
 	if errors.Is(err, storageErrs.ErrHeadBlockNotFound) {
 		return nil
 	}
 	if err != nil {
-		fmt.Printf("%s: cannot get head block", err.Error())
+		fmt.Printf("cannot get head block: %s", err.Error())
 		return nil
 	}
 
 	blocks, err := counters.Get(ctx, modules.BlockCounter)
 	if err != nil {
-		fmt.Printf("%s: cannot get block counter", err.Error())
+		fmt.Printf("cannot get block counter: %s", err.Error())
 		return nil
 	}
 
@@ -337,7 +337,7 @@ func ComputeCheckDataProgress(
 
 	orphans, err := counters.Get(ctx, modules.OrphanCounter)
 	if err != nil {
-		fmt.Printf("%s: cannot get orphan counter", err.Error())
+		fmt.Printf("cannot get orphan counter: %s", err.Error())
 		return nil
 	}
 
@@ -350,7 +350,7 @@ func ComputeCheckDataProgress(
 
 	elapsedTime, err := counters.Get(ctx, TimeElapsedCounter)
 	if err != nil {
-		fmt.Printf("%s: cannot get elapsed time", err.Error())
+		fmt.Printf("cannot get elapsed time: %s", err.Error())
 		return nil
 	}
 
diff --git a/pkg/results/perf_results.go b/pkg/results/perf_results.go
index a0349c6..9d5c7ee 100644
--- a/pkg/results/perf_results.go
+++ b/pkg/results/perf_results.go
@@ -32,7 +32,7 @@ func (c *CheckPerfStats) Output(path string) {
 	if len(path) > 0 {
 		writeErr := utils.SerializeAndWrite(path, c)
 		if writeErr != nil {
-			log.Printf("%s: unable to save results\n", writeErr.Error())
+			log.Printf("unable to save results: %s\n", writeErr.Error())
 		}
 	}
 }


Test patch:
diff --git a/pkg/tester/construction.go b/pkg/tester/construction.go
index 537890c..e6d8d46 100644
--- a/pkg/tester/construction.go
+++ b/pkg/tester/construction.go
@@ -19,11 +19,12 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	customErrs "github.com/coinbase/rosetta-cli/pkg/errors"
 	"log"
 	"net/http"
 	"time"
 
+	cliErrs "github.com/coinbase/rosetta-cli/pkg/errors"
+
 	"github.com/coinbase/rosetta-cli/configuration"
 	"github.com/coinbase/rosetta-cli/pkg/logger"
 	"github.com/coinbase/rosetta-cli/pkg/processor"
@@ -84,7 +85,7 @@ func InitializeConstruction(
 ) (*ConstructionTester, error) {
 	dataPath, err := utils.CreateCommandPath(config.DataDirectory, constructionCmdName, network)
 	if err != nil {
-		return nil, fmt.Errorf("%s: cannot create command path", err.Error())
+		return nil, fmt.Errorf("failed to create command path: %w", err)
 	}
 
 	opts := []database.BadgerOption{}
@@ -100,17 +101,17 @@ func InitializeConstruction(
 
 	localStore, err := database.NewBadgerDatabase(ctx, dataPath, opts...)
 	if err != nil {
-		return nil, fmt.Errorf("%s: unable to initialize database", err.Error())
+		return nil, fmt.Errorf("unable to initialize database: %w", err)
 	}
 
 	networkOptions, fetchErr := onlineFetcher.NetworkOptionsRetry(ctx, network, nil)
 	if err != nil {
-		return nil, fmt.Errorf("%s: unable to get network options", fetchErr.Err.Error())
+		return nil, fmt.Errorf("unable to get network options: %w", fetchErr.Err)
 	}
 
 	if len(networkOptions.Allow.BalanceExemptions) > 0 &&
 		config.Construction.InitialBalanceFetchDisabled {
-		return nil, fmt.Errorf("found balance exemptions but initial balance fetch disabled")
+		return nil, cliErrs.ErrBalanceExemptionsWithInitialBalanceFetchDisabled
 	}
 
 	counterStorage := modules.NewCounterStorage(localStore)
@@ -124,7 +125,7 @@ func InitializeConstruction(
 		network,
 	)
 	if err != nil {
-		return nil, fmt.Errorf("unable to initialize logger with error: %s", err.Error())
+		return nil, fmt.Errorf("unable to initialize logger with error: %w", err)
 	}
 
 	blockStorage := modules.NewBlockStorage(localStore, config.SerialBlockWorkers)
@@ -194,7 +195,7 @@ func InitializeConstruction(
 	// Load all accounts for network
 	accounts, err := keyStorage.GetAllAccounts(ctx)
 	if err != nil {
-		return nil, fmt.Errorf("%w: unable to load addresses", err)
+		return nil, fmt.Errorf("unable to load addresses: %w", err)
 	}
 
 	// Track balances on all addresses
@@ -230,12 +231,12 @@ func InitializeConstruction(
 
 	accBalances, err := utils.GetAccountBalances(ctx, onlineFetcher, accountBalanceRequests)
 	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get account balances", err)
+		return nil, fmt.Errorf("unable to get account balances: %w", err)
 	}
 
 	err = balanceStorage.SetBalanceImported(ctx, nil, accBalances)
 	if err != nil {
-		return nil, fmt.Errorf("%w: unable to set balances", err)
+		return nil, fmt.Errorf("unable to set balances: %w", err)
 	}
 
 	// -------------------------------------------------------------------------
@@ -245,7 +246,7 @@ func InitializeConstruction(
 	if config.CoinSupported {
 		acctCoins, errAccCoins := utils.GetAccountCoins(ctx, onlineFetcher, acctCoinsReqs)
 		if errAccCoins != nil {
-			return nil, fmt.Errorf("%w: unable to get account coins", errAccCoins)
+			return nil, fmt.Errorf("unable to get account coins: %w", errAccCoins)
 		}
 
 		// Extract accounts from account coins requests
@@ -256,7 +257,7 @@ func InitializeConstruction(
 
 		err = coinStorage.SetCoinsImported(ctx, accts, acctCoins)
 		if err != nil {
-			return nil, fmt.Errorf("%w: unable to set coin balances", err)
+			return nil, fmt.Errorf("unable to set coin balances: %w", err)
 		}
 	}
 
@@ -290,7 +291,7 @@ func InitializeConstruction(
 		config.Construction.Workflows,
 	)
 	if err != nil {
-		log.Fatalf("%s: unable to create coordinator", err.Error())
+		log.Fatalf("unable to create coordinator: %s", err.Error())
 	}
 
 	broadcastHandler := processor.NewBroadcastStorageHandler(
@@ -338,7 +339,7 @@ func InitializeConstruction(
 // CloseDatabase closes the database used by ConstructionTester.
 func (t *ConstructionTester) CloseDatabase(ctx context.Context) {
 	if err := t.database.Close(ctx); err != nil {
-		log.Fatalf("%s: error closing database", err.Error())
+		log.Fatalf("error closing database: %s", err.Error())
 	}
 }
 
@@ -375,7 +376,7 @@ func (t *ConstructionTester) checkTip(ctx context.Context) (int64, error) {
 		t.onlineFetcher,
 	)
 	if err != nil {
-		return -1, err
+		return -1, fmt.Errorf("failed to check network tip: %w", err)
 	}
 
 	if atTip {
@@ -394,7 +395,7 @@ func (t *ConstructionTester) waitForTip(ctx context.Context) (int64, error) {
 		// Don't wait any time before first tick if at tip.
 		tipIndex, err := t.checkTip(ctx)
 		if err != nil {
-			return -1, err
+			return -1, fmt.Errorf("failed to check tip: %w", err)
 		}
 
 		if tipIndex != -1 {
@@ -426,10 +427,10 @@ func (t *ConstructionTester) StartSyncer(
 		// we will unnecessarily sync tons of blocks before reaching any that matter.
 		startIndex, err = t.waitForTip(ctx)
 		if err != nil {
-			return fmt.Errorf("%w: unable to wait for tip", err)
+			return fmt.Errorf("unable to wait for tip: %w", err)
 		}
 	} else if err != nil {
-		return fmt.Errorf("%w: unable to get last block synced", err)
+		return fmt.Errorf("unable to get last block synced: %w", err)
 	}
 
 	return t.syncer.Sync(ctx, startIndex, -1)
@@ -444,7 +445,7 @@ func (t *ConstructionTester) StartConstructor(
 	if t.config.Construction.ClearBroadcasts {
 		broadcasts, err := t.broadcastStorage.ClearBroadcasts(ctx)
 		if err != nil {
-			return fmt.Errorf("%w: unable to clear broadcasts", err)
+			return fmt.Errorf("unable to clear broadcasts: %w", err)
 		}
 
 		log.Printf("cleared %d broadcasts\n", len(broadcasts))
@@ -481,7 +482,7 @@ func (t *ConstructionTester) PerformBroadcasts(ctx context.Context) error {
 	color.Magenta("Rebroadcasting all transactions...")
 
 	if err := t.broadcastStorage.BroadcastAll(ctx, false); err != nil {
-		return fmt.Errorf("%w: unable to broadcast all transactions", err)
+		return fmt.Errorf("unable to broadcast all transactions: %w", err)
 	}
 
 	return nil
@@ -510,7 +511,7 @@ func (t *ConstructionTester) WatchEndConditions(
 			for workflow, minOccurences := range endConditions {
 				completed, err := t.jobStorage.Completed(ctx, workflow)
 				if err != nil {
-					return fmt.Errorf("%w: unable to fetch completed %s", err, workflow)
+					return fmt.Errorf("unable to fetch completed %s: %w", workflow, err)
 				}
 
 				if len(completed) < minOccurences {
@@ -579,7 +580,7 @@ func (t *ConstructionTester) HandleErr(
 			t.config,
 			t.counterStorage,
 			t.jobStorage,
-			fmt.Errorf("%w: %v", customErrs.ErrConstructionCheckHalt, err.Error()),
+			fmt.Errorf("%v: %w", err.Error(), cliErrs.ErrConstructionCheckHalt),
 		)
 	}
 
diff --git a/pkg/tester/data.go b/pkg/tester/data.go
index 12ed5a9..e258469 100644
--- a/pkg/tester/data.go
+++ b/pkg/tester/data.go
@@ -19,12 +19,13 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	customErrs "github.com/coinbase/rosetta-cli/pkg/errors"
 	"log"
 	"math/big"
 	"net/http"
 	"time"
 
+	cliErrs "github.com/coinbase/rosetta-cli/pkg/errors"
+
 	"github.com/coinbase/rosetta-cli/configuration"
 	"github.com/coinbase/rosetta-cli/pkg/logger"
 	"github.com/coinbase/rosetta-cli/pkg/processor"
@@ -63,18 +64,18 @@ const (
 	PeriodicLoggingFrequency = periodicLoggingSeconds * time.Second
 
 	// EndAtTipCheckInterval is the frequency that EndAtTip condition
-	// is evaludated
+	// is evaluated
 	EndAtTipCheckInterval = 10 * time.Second
-	
+
 	//MinTableSize unit is GB
 	MinTableSize = int64(2)
-	
+
 	//MaxTableSize unit is GB
 	MaxTableSize = int64(100)
 
 	//MinTableSize unit is MB
 	MinValueLogFileSize = int64(128)
-	
+
 	//MaxTableSize unit is MB
 	MaxValueLogFileSize = int64(2048)
 )
@@ -127,7 +128,7 @@ func loadAccounts(filePath string) ([]*types.AccountCurrency, error) {
 
 	accounts := []*types.AccountCurrency{}
 	if err := utils.LoadAndParse(filePath, &accounts); err != nil {
-		return nil, fmt.Errorf("%w: unable to open account file", err)
+		return nil, fmt.Errorf("unable to load and parse %s: %w", filePath, err)
 	}
 
 	log.Printf(
@@ -143,7 +144,7 @@ func loadAccounts(filePath string) ([]*types.AccountCurrency, error) {
 // CloseDatabase closes the database used by DataTester.
 func (t *DataTester) CloseDatabase(ctx context.Context) {
 	if err := t.database.Close(ctx); err != nil {
-		log.Fatalf("%s: error closing database", err.Error())
+		log.Fatalf("error closing database: %s", err.Error())
 	}
 }
 
@@ -160,13 +161,13 @@ func InitializeData(
 ) (*DataTester, error) {
 	dataPath, err := utils.CreateCommandPath(config.DataDirectory, dataCmdName, network)
 	if err != nil {
-		return nil, fmt.Errorf("%s: cannot create command path", err.Error())
+		return nil, fmt.Errorf("failed to create command path: %w", err)
 	}
 
 	opts := []database.BadgerOption{}
 	dataPathBackup := dataPath
 
-	if config.AllInMemoryEnabled{
+	if config.AllInMemoryEnabled {
 		opts = append(
 			opts,
 			database.WithCustomSettings(database.AllInMemoryBadgerOptions(dataPath)),
@@ -187,20 +188,20 @@ func InitializeData(
 	}
 
 	// If we enable all-in-memory or L0-in-memory mode, badger DB's TableSize and ValueLogFileSize will change
-	// according to users config. tableSize means the LSM table size, when the table more than the tableSize, 
-	// will trigger a compact. 
+	// according to users config. tableSize means the LSM table size, when the table more than the tableSize,
+	// will trigger a compact.
 	// In default mode, we will not change the badger DB's TableSize and ValueLogFileSize for limiting memory usage
 	if config.AllInMemoryEnabled || config.MemoryLimitDisabled {
-		if(config.TableSize != nil) {
-			if(*config.TableSize >= MinTableSize && *config.TableSize <= MaxTableSize) {
+		if config.TableSize != nil {
+			if *config.TableSize >= MinTableSize && *config.TableSize <= MaxTableSize {
 				opts = append(
 					opts,
 					database.WithTableSize(*config.TableSize),
 				)
 			}
 		}
-		if(config.ValueLogFileSize != nil) {
-			if(*config.TableSize >= MinValueLogFileSize && *config.TableSize <= MinValueLogFileSize) {
+		if config.ValueLogFileSize != nil {
+			if *config.TableSize >= MinValueLogFileSize && *config.TableSize <= MinValueLogFileSize {
 				opts = append(
 					opts,
 					database.WithValueLogFileSize(*config.TableSize),
@@ -211,17 +212,17 @@ func InitializeData(
 
 	localStore, err := database.NewBadgerDatabase(ctx, dataPathBackup, opts...)
 	if err != nil {
-		return nil, fmt.Errorf("%s: unable to initialize database", err.Error())
+		return nil, fmt.Errorf("unable to initialize database: %w", err)
 	}
 
 	exemptAccounts, err := loadAccounts(config.Data.ExemptAccounts)
 	if err != nil {
-		return nil, fmt.Errorf("%s: unable to load exempt accounts", err.Error())
+		return nil, fmt.Errorf("unable to load exempt accounts: %w", err)
 	}
 
 	interestingAccounts, err := loadAccounts(config.Data.InterestingAccounts)
 	if err != nil {
-		return nil, fmt.Errorf("%s: unable to load interesting accounts", err.Error())
+		return nil, fmt.Errorf("unable to load interesting accounts: %w", err)
 	}
 
 	counterStorage := modules.NewCounterStorage(localStore)
@@ -238,7 +239,7 @@ func InitializeData(
 		network,
 	)
 	if err != nil {
-		return nil, fmt.Errorf("unable to initialize logger with error: %s", err.Error())
+		return nil, fmt.Errorf("unable to initialize logger with error: %w", err)
 	}
 
 	var forceInactiveReconciliation bool
@@ -262,12 +263,12 @@ func InitializeData(
 	// Get all previously seen accounts
 	seenAccounts, err := balanceStorage.GetAllAccountCurrency(ctx)
 	if err != nil {
-		return nil, fmt.Errorf("%s: unable to get previously seen accounts", err.Error())
+		return nil, fmt.Errorf("unable to get previously seen accounts: %w", err)
 	}
 
 	networkOptions, fetchErr := fetcher.NetworkOptionsRetry(ctx, network, nil)
 	if fetchErr != nil {
-		log.Fatalf("%s: unable to get network options", fetchErr.Err.Error())
+		log.Fatalf("unable to get network options: %s", fetchErr.Err.Error())
 	}
 
 	if len(networkOptions.Allow.BalanceExemptions) > 0 && config.Data.InitialBalanceFetchDisabled {
@@ -354,10 +355,10 @@ func InitializeData(
 					genesisBlock,
 				)
 				if err != nil {
-					return nil, fmt.Errorf("%s: unable to bootstrap balances", err.Error())
+					return nil, fmt.Errorf("unable to bootstrap balances: %w", err)
 				}
 			case err != nil:
-				return nil, fmt.Errorf("%s: unable to get head block identifier", err.Error())
+				return nil, fmt.Errorf("unable to get head block identifier: %w", err)
 			default:
 				log.Println("Skipping balance bootstrapping because already started syncing")
 			}
@@ -548,7 +549,7 @@ func (t *DataTester) syncedStatus(ctx context.Context) (bool, int64, error) {
 		t.blockStorage,
 	)
 	if err != nil {
-		return false, -1, err
+		return false, -1, fmt.Errorf("failed to check storage tip: %w", err)
 	}
 
 	var blockIndex int64 = -1
@@ -576,7 +577,7 @@ func (t *DataTester) EndAtTipLoop(
 			atTip, blockIndex, err := t.syncedStatus(ctx)
 			if err != nil {
 				log.Printf(
-					"%s: unable to evaluate if syncer is at tip",
+					"unable to evaluate if syncer is at tip: %s",
 					err.Error(),
 				)
 				continue
@@ -614,7 +615,7 @@ func (t *DataTester) EndReconciliationCoverage( // nolint:gocognit
 			atTip, blockIndex, err := t.syncedStatus(ctx)
 			if err != nil {
 				log.Printf(
-					"%s: unable to evaluate syncer height or if at tip",
+					"unable to evaluate syncer height or if at tip: %s",
 					err.Error(),
 				)
 				continue
@@ -675,7 +676,7 @@ func (t *DataTester) EndReconciliationCoverage( // nolint:gocognit
 				allAccounts, err := t.balanceStorage.GetAllAccountCurrency(ctx)
 				if err != nil {
 					log.Printf(
-						"%s: unable to get account count",
+						"unable to get account count: %s",
 						err.Error(),
 					)
 					continue
@@ -694,7 +695,7 @@ func (t *DataTester) EndReconciliationCoverage( // nolint:gocognit
 			coverage, err := t.balanceStorage.ReconciliationCoverage(ctx, coverageIndex)
 			if err != nil {
 				log.Printf(
-					"%s: unable to get reconciliation coverage",
+					"unable to get reconciliation coverage: %s",
 					err.Error(),
 				)
 				continue
@@ -776,22 +777,22 @@ func (t *DataTester) WatchEndConditions(
 func (t *DataTester) CompleteReconciliations(ctx context.Context) (int64, error) {
 	activeReconciliations, err := t.counterStorage.Get(ctx, modules.ActiveReconciliationCounter)
 	if err != nil {
-		return -1, fmt.Errorf("%w: cannot get active reconciliations counter", err)
+		return -1, fmt.Errorf("failed to get active reconciliations counter: %w", err)
 	}
 
 	exemptReconciliations, err := t.counterStorage.Get(ctx, modules.ExemptReconciliationCounter)
 	if err != nil {
-		return -1, fmt.Errorf("%w: cannot get exempt reconciliations counter", err)
+		return -1, fmt.Errorf("failed to get exempt reconciliations counter: %w", err)
 	}
 
 	failedReconciliations, err := t.counterStorage.Get(ctx, modules.FailedReconciliationCounter)
 	if err != nil {
-		return -1, fmt.Errorf("%w: cannot get failed reconciliations counter", err)
+		return -1, fmt.Errorf("failed to get failed reconciliations counter: %w", err)
 	}
 
 	skippedReconciliations, err := t.counterStorage.Get(ctx, modules.SkippedReconciliationsCounter)
 	if err != nil {
-		return -1, fmt.Errorf("%w: cannot get skipped reconciliations counter", err)
+		return -1, fmt.Errorf("failed to get skipped reconciliations counter: %w", err)
 	}
 
 	return activeReconciliations.Int64() +
@@ -811,7 +812,7 @@ func (t *DataTester) WaitForEmptyQueue(
 	// and only exit when that many reconciliations have been performed.
 	startingComplete, err := t.CompleteReconciliations(ctx)
 	if err != nil {
-		return err
+		return fmt.Errorf("failed to complete reconciliations: %w", err)
 	}
 	startingRemaining := t.reconciler.QueueSize()
 
@@ -832,12 +833,12 @@ func (t *DataTester) WaitForEmptyQueue(
 			// We force cached counts to be written before
 			// determining if we should exit.
 			if err := t.reconcilerHandler.UpdateCounts(ctx); err != nil {
-				return err
+				return fmt.Errorf("failed to update count: %w", err)
 			}
 
 			nowComplete, err := t.CompleteReconciliations(ctx)
 			if err != nil {
-				return err
+				return fmt.Errorf("failed to complete reconciliations: %w", err)
 			}
 
 			completed := nowComplete - startingComplete
@@ -882,7 +883,7 @@ func (t *DataTester) DrainReconcilerQueue(
 	err := g.Wait()
 
 	if *t.signalReceived {
-		return errors.New("reconcilier queue drain halted")
+		return cliErrs.ErrReconcilerDrainHalt
 	}
 
 	if errors.Is(err, context.Canceled) {
@@ -906,7 +907,7 @@ func (t *DataTester) HandleErr(err error, sigListeners *[]context.CancelFunc) er
 			t.config,
 			t.counterStorage,
 			t.balanceStorage,
-			fmt.Errorf("%w: %v", customErrs.ErrDataCheckHalt, err.Error()),
+			fmt.Errorf("%v: %w", err.Error(), cliErrs.ErrDataCheckHalt),
 			"",
 			"",
 		)
@@ -1015,7 +1016,7 @@ func (t *DataTester) FindMissingOps(
 		t.reconcilerHandler.InactiveFailureBlock.Index,
 	)
 	if err != nil {
-		color.Yellow("%s: could not find block with missing ops", err.Error())
+		color.Yellow("could not find block with missing ops: %s", err.Error())
 		return results.ExitData(
 			t.config,
 			t.counterStorage,
@@ -1057,13 +1058,13 @@ func (t *DataTester) recursiveOpSearch(
 	// Always use a temporary directory to find missing ops
 	tmpDir, err := utils.CreateTempDir()
 	if err != nil {
-		return nil, fmt.Errorf("%w: unable to create temporary directory", err)
+		return nil, fmt.Errorf("unable to create temporary directory: %w", err)
 	}
 	defer utils.RemoveTempDir(tmpDir)
 
 	localStore, err := database.NewBadgerDatabase(ctx, tmpDir)
 	if err != nil {
-		return nil, fmt.Errorf("%w: unable to initialize database", err)
+		return nil, fmt.Errorf("unable to initialize database: %w", err)
 	}
 
 	counterStorage := modules.NewCounterStorage(localStore)
@@ -1081,7 +1082,7 @@ func (t *DataTester) recursiveOpSearch(
 	)
 
 	if err != nil {
-		return nil, fmt.Errorf("unable to initialize logger with error: %s", err.Error())
+		return nil, fmt.Errorf("unable to initialize logger with error: %w", err)
 	}
 
 	t.forceInactiveReconciliation = types.Bool(false)
@@ -1173,16 +1174,16 @@ func (t *DataTester) recursiveOpSearch(
 	// Close database before starting another search, otherwise we will
 	// have n databases open when we find the offending block.
 	if storageErr := localStore.Close(ctx); storageErr != nil {
-		return nil, fmt.Errorf("%w: unable to close database", storageErr)
+		return nil, fmt.Errorf("unable to close database: %w", storageErr)
 	}
 
 	if *t.signalReceived {
-		return nil, errors.New("search for block with missing ops halted")
+		return nil, cliErrs.ErrMissingOps
 	}
 
 	if err == nil || errors.Is(err, context.Canceled) {
 		if startIndex <= t.genesisBlock.Index {
-			return nil, errors.New("unable to find missing ops")
+			return nil, cliErrs.ErrUnableToFindMissingOps
 		}
 
 		newStart := startIndex - InactiveFailureLookbackWindow
@@ -1193,7 +1194,7 @@ func (t *DataTester) recursiveOpSearch(
 		newEnd := endIndex - InactiveFailureLookbackWindow
 		if newEnd <= newStart {
 			return nil, fmt.Errorf(
-				"Next window to check has start index %d <= end index %d",
+				"next window to check has start index %d <= end index %d",
 				newStart,
 				newEnd,
 			)
@@ -1219,7 +1220,7 @@ func (t *DataTester) recursiveOpSearch(
 	}
 
 	if reconcilerHandler.ActiveFailureBlock == nil {
-		return nil, errors.New("unable to find missing ops")
+		return nil, cliErrs.ErrUnableToFindMissingOps
 	}
 
 	return reconcilerHandler.ActiveFailureBlock, nil
diff --git a/pkg/tester/data_perf.go b/pkg/tester/data_perf.go
index 0cdefc5..0ce279a 100644
--- a/pkg/tester/data_perf.go
+++ b/pkg/tester/data_perf.go
@@ -16,10 +16,10 @@ package tester
 
 import (
 	"context"
-	"errors"
 	"time"
 
 	"github.com/coinbase/rosetta-cli/configuration"
+	cliErrs "github.com/coinbase/rosetta-cli/pkg/errors"
 	"github.com/coinbase/rosetta-cli/pkg/results"
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/types"
@@ -56,7 +56,7 @@ func BmarkBlock(ctx context.Context, config *configuration.Configuration, fetche
 	}()
 	select {
 	case <-ctx.Done():
-		return errors.New("/block endpoint benchmarking timed out")
+		return cliErrs.ErrBlockBenchmarkTimeout
 	case timeTaken := <-elapsed:
 		rawStats.BlockEndpointTotalTime = timeTaken
 		rawStats.BlockEndpointNumErrors = int64(total_errors)
@@ -87,7 +87,7 @@ func BmarkAccountBalance(ctx context.Context, config *configuration.Configuratio
 	}()
 	select {
 	case <-ctx.Done():
-		return errors.New("/account/balance endpoint benchmarking timed out")
+		return cliErrs.ErrAccountBalanceBenchmarkTimeout
 	case timeTaken := <-elapsed:
 		rawStats.AccountBalanceEndpointTotalTime = timeTaken
 		rawStats.AccountBalanceNumErrors = int64(total_errors)