You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by kl...@apache.org on 2022/09/02 15:45:08 UTC

[incubator-devlake] branch main updated: [feat-2134][framework]: Rework the error management code in the Backend (#2763)

This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 8595fd47 [feat-2134][framework]: Rework the error management code in the Backend (#2763)
8595fd47 is described below

commit 8595fd47326012ea347bf78fdda9c155b3a2c716
Author: Keon Amini <ke...@merico.dev>
AuthorDate: Fri Sep 2 10:45:04 2022 -0500

    [feat-2134][framework]: Rework the error management code in the Backend (#2763)
    
    * feat: trying out https://github.com/joomcode/errorx
    
    * feat: trying out https://github.com/cockroachdb/errors
    
    * refactor: added options for 'user friendly' messages
    
    * fix: added optional stacktraces, fixed a bug with New
    
    * refactor: standardized Message API formatting
    
    * refactor: using global flag for enabling stacktraces now
    
    * refactor: replacing error instances with new error type
    
    * feat: WrapRaw method added for convenience
    
    * feat: added extra error types and error caching for runtime lookups
    
    * refactor: transformed all error usages to errors.Error
    
    * test: errors test added
    
    * fix: any -> interface{} to make github build happy
    
    * refactor: log API extended for wrapped errors + fixed a lot of broken error messages
    
    * refactor: added detailed error messages for plugin APIs and framework
    
    * refactor: .env.example updated with new ENABLE_STACKTRACE flag
---
 .env.example                                       |   1 +
 api/api.go                                         |  10 +-
 api/blueprints/blueprints.go                       |  31 ++--
 api/domainlayer/repos.go                           |   3 +-
 api/pipelines/pipelines.go                         |  31 ++--
 api/plugininfo/plugininifo.go                      |   3 +-
 api/push/push.go                                   |   6 +-
 api/router.go                                      |  82 +++++-----
 api/shared/api_output.go                           |  28 +++-
 api/task/task.go                                   |  11 +-
 config/config.go                                   |  11 +-
 errors/errors.go                                   |  58 ++++---
 errors/errors_test.go                              |  91 +++++++++++
 errors/impl.go                                     | 167 +++++++++++++++++++++
 errors/sub_task_error.go                           |  33 ----
 errors/types.go                                    | 143 ++++++++++++++++++
 generator/cmd/create_collector.go                  |  14 +-
 generator/cmd/create_extractor.go                  |   8 +-
 generator/cmd/create_migration.go                  |   8 +-
 generator/cmd/create_plugin.go                     |  12 +-
 generator/cmd/e2e_raw_create.go                    |  10 +-
 generator/cmd/init_migration.go                    |   4 +-
 go.mod                                             |  12 +-
 go.sum                                             | 162 +++++++++++++++++++-
 helpers/e2ehelper/data_flow_tester.go              |  17 ++-
 helpers/pluginhelper/csv_file_iterator.go          |   5 +-
 helpers/pluginhelper/csv_file_writer.go            |   5 +-
 logger/logger.go                                   |  21 ++-
 models/domainlayer/didgen/domain_id_generator.go   |   7 +-
 plugins/ae/api/connection.go                       |   5 +-
 plugins/ae/impl/impl.go                            |   5 +-
 plugins/ae/tasks/commits_extractor.go              |   1 -
 plugins/azure/api/blueprint.go                     |   1 -
 plugins/azure/api/connection.go                    |  20 +--
 plugins/azure/impl/impl.go                         |   5 +-
 plugins/azure/tasks/api_client.go                  |   3 +-
 plugins/azure/tasks/repo_extractor.go              |   3 +-
 plugins/azure/tasks/task_data.go                   |   4 +-
 plugins/bitbucket/api/blueprint.go                 |   5 +-
 plugins/bitbucket/api/connection.go                |   7 +-
 plugins/bitbucket/impl/impl.go                     |   6 +-
 plugins/bitbucket/tasks/api_client.go              |   5 +-
 plugins/bitbucket/tasks/api_common.go              |   7 +-
 plugins/bitbucket/tasks/repo_extractor.go          |   3 +-
 plugins/bitbucket/tasks/task_data.go               |   8 +-
 plugins/bitbucket/utils/utils.go                   |  14 +-
 plugins/core/hub.go                                |   8 +-
 plugins/core/logger.go                             |  14 +-
 plugins/core/plugin_api.go                         |  19 +--
 plugins/core/plugin_utils.go                       |  13 +-
 plugins/dbt/dbt.go                                 |   8 +-
 plugins/dbt/tasks/convertor.go                     |   8 +-
 plugins/dora/impl/impl.go                          |   3 +-
 plugins/feishu/api/connection.go                   |   8 +-
 plugins/feishu/impl/impl.go                        |   3 +-
 .../migrationscripts/20220714_add_init_tables.go   |   1 -
 plugins/feishu/tasks/api_client.go                 |   3 +-
 plugins/gitee/api/connection.go                    |   8 +-
 plugins/gitee/impl/impl.go                         |   9 +-
 plugins/gitee/tasks/api_client.go                  |   6 +-
 plugins/gitee/tasks/commit_collector.go            |   4 +-
 plugins/gitee/tasks/commit_extractor.go            |   1 -
 plugins/gitee/tasks/commit_stats_collector.go      |   3 +-
 plugins/gitee/tasks/commit_stats_extractor.go      |   1 -
 plugins/gitee/tasks/issue_collector.go             |   3 +-
 plugins/gitee/tasks/issue_comment_collector.go     |   5 +-
 plugins/gitee/tasks/issue_comment_extractor.go     |   1 -
 plugins/gitee/tasks/issue_extractor.go             |  18 +--
 plugins/gitee/tasks/pr_collector.go                |   3 +-
 plugins/gitee/tasks/pr_extractor.go                |  10 +-
 plugins/gitee/tasks/pr_issue_enricher.go           |   5 +-
 plugins/gitee/tasks/repo_extractor.go              |   3 +-
 plugins/gitee/tasks/shared.go                      |  18 +--
 plugins/gitextractor/main.go                       |   1 -
 plugins/gitextractor/parser/repo.go                |   6 +-
 plugins/gitextractor/tasks/git_repo_collector.go   |  16 +-
 plugins/github/api/blueprint.go                    |   7 +-
 plugins/github/api/connection.go                   |  15 +-
 ...raw_github_api_pull_request_review_comments.csv |   2 +-
 plugins/github/impl/impl.go                        |   9 +-
 .../migrationscripts/20220715_add_init_tables.go   |   1 -
 .../migrationscripts/20220728_add_runs_table.go    |   4 +-
 .../migrationscripts/20220729_add_jobs_table.go    |   4 +-
 .../20220802_add_pipeline_table.go                 |   4 +-
 plugins/github/tasks/api_client.go                 |   9 +-
 plugins/github/tasks/cicd_job_extractor.go         |   1 -
 plugins/github/tasks/cicd_run_enricher.go          |   1 -
 plugins/github/tasks/cicd_run_extractor.go         |   1 -
 plugins/github/tasks/comment_collector.go          |   5 +-
 plugins/github/tasks/commit_collector.go           |   3 +-
 plugins/github/tasks/commit_extractor.go           |   1 -
 plugins/github/tasks/commit_stats_collector.go     |   3 +-
 plugins/github/tasks/commit_stats_extractor.go     |   1 -
 plugins/github/tasks/event_collector.go            |   3 +-
 plugins/github/tasks/event_extractor.go            |   1 -
 plugins/github/tasks/issue_collector.go            |   3 +-
 plugins/github/tasks/issue_extractor.go            |  18 +--
 plugins/github/tasks/pr_collector.go               |   3 +-
 plugins/github/tasks/pr_extractor.go               |  10 +-
 plugins/github/tasks/pr_issue_enricher.go          |   5 +-
 plugins/github/tasks/pr_review_collector.go        |   3 +-
 .../github/tasks/pr_review_comment_collector.go    |   3 +-
 .../github/tasks/pr_review_comment_extractor.go    |   6 +-
 plugins/github/tasks/repo_extractor.go             |   3 +-
 plugins/github/tasks/task_data.go                  |   8 +-
 plugins/github/utils/utils.go                      |  15 +-
 plugins/github_graphql/plugin_main.go              |  10 +-
 plugins/github_graphql/tasks/pr_collector.go       |   7 +-
 plugins/gitlab/api/blueprint.go                    |   6 +-
 plugins/gitlab/api/connection.go                   |   7 +-
 plugins/gitlab/impl/impl.go                        |   5 +-
 .../migrationscripts/20220714_add_init_tables.go   |   1 -
 plugins/gitlab/tasks/account_collector.go          |   2 +-
 plugins/gitlab/tasks/account_extractor.go          |   1 -
 plugins/gitlab/tasks/api_client.go                 |   3 +-
 plugins/gitlab/tasks/commit_extractor.go           |   1 -
 plugins/gitlab/tasks/issue_collector.go            |   3 +-
 plugins/gitlab/tasks/issue_extractor.go            |  18 +--
 plugins/gitlab/tasks/job_extractor.go              |   1 -
 plugins/gitlab/tasks/mr_extractor.go               |  10 +-
 plugins/gitlab/tasks/pipeline_extractor.go         |   1 -
 plugins/gitlab/tasks/project_extractor.go          |   1 -
 plugins/gitlab/tasks/shared.go                     |   7 +-
 plugins/gitlab/tasks/tag_extractor.go              |   1 -
 plugins/gitlab/tasks/task_data.go                  |   8 +-
 plugins/helper/api_async_client.go                 |  22 +--
 plugins/helper/api_client.go                       |  49 +++---
 plugins/helper/api_collector.go                    |  29 ++--
 plugins/helper/api_collector_func.go               |   7 +-
 plugins/helper/api_extractor.go                    |  13 +-
 plugins/helper/api_rawdata.go                      |   9 +-
 plugins/helper/batch_save.go                       |   9 +-
 plugins/helper/batch_save_divider.go               |   3 +-
 plugins/helper/config_util.go                      |   5 +-
 plugins/helper/connection.go                       |  13 +-
 plugins/helper/data_convertor.go                   |  24 +--
 plugins/helper/default_task_context.go             |   3 +-
 plugins/helper/graphql_async_client.go             |   6 +-
 plugins/helper/graphql_collector.go                |  26 ++--
 plugins/helper/pipeline_plan.go                    |   3 +-
 plugins/helper/worker_scheduler.go                 |  10 +-
 plugins/icla/plugin_main.go                        |   3 +-
 plugins/jenkins/api/blueprint.go                   |   1 -
 plugins/jenkins/api/connection.go                  |   3 +-
 plugins/jenkins/impl/impl.go                       |   5 +-
 plugins/jenkins/tasks/job_collector.go             |   5 +-
 plugins/jenkins/tasks/task_data.go                 |   6 +-
 plugins/jira/api/blueprint.go                      |   1 -
 plugins/jira/api/connection.go                     |  22 +--
 plugins/jira/impl/impl.go                          |  17 ++-
 .../migrationscripts/20220407_add_source_table.go  |   1 -
 .../20220505_rename_source_table.go                |   1 -
 .../migrationscripts/20220716_add_init_tables.go   |  10 +-
 plugins/jira/tasks/account_collector.go            |   2 +-
 plugins/jira/tasks/account_extractor.go            |   1 -
 plugins/jira/tasks/api_client.go                   |   5 +-
 plugins/jira/tasks/board_collector.go              |   2 +-
 plugins/jira/tasks/board_extractor.go              |   1 -
 plugins/jira/tasks/epic_collector.go               |   3 +-
 plugins/jira/tasks/issue_changelog_convertor.go    |   2 +-
 plugins/jira/tasks/issue_changelog_extractor.go    |   1 -
 plugins/jira/tasks/issue_collector.go              |   3 +-
 plugins/jira/tasks/issue_repo_commit_convertor.go  |   8 +-
 plugins/jira/tasks/issue_type_extractor.go         |   1 -
 plugins/jira/tasks/project_collector.go            |   2 +-
 plugins/jira/tasks/project_extractor.go            |   1 -
 plugins/jira/tasks/remotelink_collector.go         |   2 +-
 plugins/jira/tasks/remotelink_extractor.go         |   8 +-
 plugins/jira/tasks/status_extractor.go             |   1 -
 plugins/jira/tasks/task_data.go                    |   5 +-
 plugins/jira/tasks/worklog_collector.go            |   2 +-
 plugins/jira/tasks/worklog_convertor.go            |   2 +-
 plugins/org/api/handlers.go                        |   4 +-
 plugins/org/impl/impl.go                           |   3 +-
 .../refdiff/tasks/ref_commit_diff_calculator.go    |   3 +-
 plugins/refdiff/tasks/refdiff_task_data.go         |  11 +-
 .../tasks/refs_pr_cherry_pick_calculator.go        |   5 +-
 plugins/starrocks/tasks.go                         |  15 +-
 plugins/tapd/api/connection.go                     |  11 +-
 plugins/tapd/impl/impl.go                          |   9 +-
 plugins/tapd/models/connection.go                  |   1 -
 plugins/tapd/models/task_changelog.go              |   1 -
 plugins/tapd/tasks/account_collector.go            |   2 +-
 plugins/tapd/tasks/account_extractor.go            |   1 -
 plugins/tapd/tasks/api_client.go                   |   3 +-
 plugins/tapd/tasks/bug_changelog_collector.go      |   5 +-
 plugins/tapd/tasks/bug_changelog_extractor.go      |   1 -
 plugins/tapd/tasks/bug_collector.go                |   5 +-
 plugins/tapd/tasks/bug_commit_collector.go         |   5 +-
 plugins/tapd/tasks/bug_commit_extractor.go         |   1 -
 plugins/tapd/tasks/bug_custom_fields_collector.go  |   2 +-
 plugins/tapd/tasks/bug_custom_fields_extractor.go  |   1 -
 plugins/tapd/tasks/bug_status_collector.go         |   2 +-
 plugins/tapd/tasks/bug_status_extractor.go         |   1 -
 plugins/tapd/tasks/company_collector.go            |   2 +-
 plugins/tapd/tasks/iteration_collector.go          |   5 +-
 plugins/tapd/tasks/iteration_extractor.go          |   1 -
 plugins/tapd/tasks/story_bug_collector.go          |   2 +-
 plugins/tapd/tasks/story_bug_extractor.go          |   1 -
 plugins/tapd/tasks/story_category_collector.go     |   2 +-
 plugins/tapd/tasks/story_category_extractor.go     |   1 -
 plugins/tapd/tasks/story_changelog_collector.go    |   5 +-
 plugins/tapd/tasks/story_collector.go              |   5 +-
 plugins/tapd/tasks/story_commit_collector.go       |   5 +-
 plugins/tapd/tasks/story_commit_extractor.go       |   1 -
 .../tapd/tasks/story_custom_fields_collector.go    |   2 +-
 .../tapd/tasks/story_custom_fields_extractor.go    |   1 -
 plugins/tapd/tasks/story_status_collector.go       |   2 +-
 plugins/tapd/tasks/story_status_extractor.go       |   1 -
 plugins/tapd/tasks/sub_workspace_collector.go      |   2 +-
 plugins/tapd/tasks/sub_workspace_extractor.go      |   1 -
 plugins/tapd/tasks/task_changelog_collector.go     |   5 +-
 plugins/tapd/tasks/task_collector.go               |   5 +-
 plugins/tapd/tasks/task_commit_collector.go        |   5 +-
 plugins/tapd/tasks/task_commit_extractor.go        |   1 -
 plugins/tapd/tasks/task_custom_fields_collector.go |   2 +-
 plugins/tapd/tasks/task_custom_fields_extractor.go |   1 -
 plugins/tapd/tasks/worklog_collector.go            |   5 +-
 plugins/tapd/tasks/worklog_extractor.go            |   1 -
 runner/db.go                                       |   7 +-
 runner/directrun.go                                |   4 +-
 runner/loader.go                                   |   3 +-
 runner/run_pipeline.go                             |   6 +-
 runner/run_task.go                                 |  47 +++---
 services/blueprint.go                              |  40 ++---
 services/pipeline.go                               |  44 +++---
 services/pipeline_runner.go                        |  11 +-
 services/task.go                                   |  20 +--
 utils/io.go                                        |  11 +-
 utils/network_helper.go                            |   7 +-
 worker/app/logger.go                               |   1 -
 worker/app/pipeline_workflow.go                    |   7 +-
 worker/app/shared.go                               |   1 -
 worker/app/task_activity.go                        |   3 +-
 234 files changed, 1417 insertions(+), 859 deletions(-)

diff --git a/.env.example b/.env.example
index 42f9a303..42cbdd9a 100644
--- a/.env.example
+++ b/.env.example
@@ -28,6 +28,7 @@ TEMPORAL_TASK_QUEUE=
 # Debug Info Warn Error
 LOGGING_LEVEL=
 LOGGING_DIR=
+ENABLE_STACKTRACE=false
 
 ##########################
 # Sensitive information encryption key
diff --git a/api/api.go b/api/api.go
index e61934e9..1ef47e10 100644
--- a/api/api.go
+++ b/api/api.go
@@ -18,7 +18,7 @@ limitations under the License.
 package api
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"time"
 
@@ -56,12 +56,12 @@ func CreateApiService() {
 	// Wait for user confirmation if db migration is needed
 	router.GET("/proceed-db-migration", func(ctx *gin.Context) {
 		if !services.MigrationRequireConfirmation() {
-			shared.ApiOutputError(ctx, fmt.Errorf("no pending migration"), http.StatusBadRequest)
+			shared.ApiOutputError(ctx, errors.BadInput.New("no pending migration", errors.AsUserMessage()))
 			return
 		}
 		err := services.ExecuteMigration()
 		if err != nil {
-			shared.ApiOutputError(ctx, err, http.StatusBadRequest)
+			shared.ApiOutputError(ctx, errors.Default.Wrap(err, "error executing migration", errors.AsUserMessage()))
 			return
 		}
 		shared.ApiOutputSuccess(ctx, nil, http.StatusOK)
@@ -72,11 +72,9 @@ func CreateApiService() {
 		}
 		shared.ApiOutputError(
 			ctx,
-			fmt.Errorf(DB_MIGRATION_REQUIRED),
-			http.StatusPreconditionRequired,
+			errors.HttpStatus(http.StatusPreconditionRequired).New(DB_MIGRATION_REQUIRED, errors.AsUserMessage()),
 		)
 		ctx.Abort()
-		return
 	})
 
 	router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
diff --git a/api/blueprints/blueprints.go b/api/blueprints/blueprints.go
index b6e9c612..ef423420 100644
--- a/api/blueprints/blueprints.go
+++ b/api/blueprints/blueprints.go
@@ -18,6 +18,7 @@ limitations under the License.
 package blueprints
 
 import (
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"strconv"
 
@@ -41,13 +42,13 @@ func Post(c *gin.Context) {
 
 	err := c.ShouldBind(blueprint)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad request body format", errors.AsUserMessage()))
 		return
 	}
 
 	err = services.CreateBlueprint(blueprint)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error creating blueprint", errors.AsUserMessage()))
 		return
 	}
 
@@ -66,12 +67,12 @@ func Index(c *gin.Context) {
 	var query services.BlueprintQuery
 	err := c.ShouldBindQuery(&query)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad request body format", errors.AsUserMessage()))
 		return
 	}
 	blueprints, count, err := services.GetBlueprints(&query)
 	if err != nil {
-		_ = c.AbortWithError(http.StatusBadRequest, err)
+		shared.ApiOutputAbort(c, errors.Default.Wrap(err, "error getting blueprints", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, gin.H{"blueprints": blueprints, "count": count}, http.StatusOK)
@@ -90,12 +91,12 @@ func Get(c *gin.Context) {
 	blueprintId := c.Param("blueprintId")
 	id, err := strconv.ParseUint(blueprintId, 10, 64)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad blueprintId format supplied", errors.AsUserMessage()))
 		return
 	}
 	blueprint, err := services.GetBlueprint(id)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error getting blueprint", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, blueprint, http.StatusOK)
@@ -113,12 +114,12 @@ func Delete(c *gin.Context) {
 	pipelineId := c.Param("blueprintId")
 	id, err := strconv.ParseUint(pipelineId, 10, 64)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad blueprintID format supplied", errors.AsUserMessage()))
 		return
 	}
 	err = services.DeleteBlueprint(id)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error deleting blueprint", errors.AsUserMessage()))
 	}
 }
 
@@ -160,18 +161,18 @@ func Patch(c *gin.Context) {
 	blueprintId := c.Param("blueprintId")
 	id, err := strconv.ParseUint(blueprintId, 10, 64)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad pipeline ID format supplied", errors.AsUserMessage()))
 		return
 	}
 	var body map[string]interface{}
 	err = c.ShouldBind(&body)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad request body format", errors.AsUserMessage()))
 		return
 	}
 	blueprint, err := services.PatchBlueprint(id, body)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error patching the blueprint", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, blueprint, http.StatusOK)
@@ -190,12 +191,12 @@ func Trigger(c *gin.Context) {
 	blueprintId := c.Param("blueprintId")
 	id, err := strconv.ParseUint(blueprintId, 10, 64)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad blueprintID format supplied", errors.AsUserMessage()))
 		return
 	}
 	pipeline, err := services.TriggerBlueprint(id)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error triggering blueprint", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, pipeline, http.StatusOK)
@@ -214,14 +215,14 @@ func GetBlueprintPipelines(c *gin.Context) {
 	blueprintId := c.Param("blueprintId")
 	id, err := strconv.ParseUint(blueprintId, 10, 64)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad blueprintID at supplied", errors.AsUserMessage()))
 		return
 	}
 	var query services.PipelineQuery
 	query.BlueprintId = id
 	pipelines, count, err := services.GetPipelines(&query)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error getting pipelines", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, shared.ResponsePipelines{Pipelines: pipelines, Count: count}, http.StatusOK)
diff --git a/api/domainlayer/repos.go b/api/domainlayer/repos.go
index 7b147b38..67ee82b8 100644
--- a/api/domainlayer/repos.go
+++ b/api/domainlayer/repos.go
@@ -18,6 +18,7 @@ limitations under the License.
 package domainlayer
 
 import (
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 
 	"github.com/apache/incubator-devlake/api/shared"
@@ -46,7 +47,7 @@ GET /repos
 func ReposIndex(c *gin.Context) {
 	repos, count, err := services.GetRepos()
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error getting repositories", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, gin.H{"repos": repos, "count": count}, http.StatusOK)
diff --git a/api/pipelines/pipelines.go b/api/pipelines/pipelines.go
index 377d1e70..74e70e7f 100644
--- a/api/pipelines/pipelines.go
+++ b/api/pipelines/pipelines.go
@@ -18,7 +18,6 @@ limitations under the License.
 package pipelines
 
 import (
-	goerror "errors"
 	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"os"
@@ -57,14 +56,14 @@ func Post(c *gin.Context) {
 
 	err := c.MustBindWith(newPipeline, binding.JSON)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad JSON request body format", errors.AsUserMessage()))
 		return
 	}
 
 	pipeline, err := services.CreatePipeline(newPipeline)
 	// Return all created tasks to the User
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error creating pipeline", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, pipeline, http.StatusCreated)
@@ -96,12 +95,12 @@ func Index(c *gin.Context) {
 	var query services.PipelineQuery
 	err := c.ShouldBindQuery(&query)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad request body format", errors.AsUserMessage()))
 		return
 	}
 	pipelines, count, err := services.GetPipelines(&query)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error getting pipelines", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, shared.ResponsePipelines{Pipelines: pipelines, Count: count}, http.StatusOK)
@@ -134,12 +133,12 @@ func Get(c *gin.Context) {
 	pipelineId := c.Param("pipelineId")
 	id, err := strconv.ParseUint(pipelineId, 10, 64)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad pipelineID format supplied", errors.AsUserMessage()))
 		return
 	}
 	pipeline, err := services.GetPipeline(id)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error getting pipeline", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, pipeline, http.StatusOK)
@@ -161,12 +160,12 @@ func Delete(c *gin.Context) {
 	pipelineId := c.Param("pipelineId")
 	id, err := strconv.ParseUint(pipelineId, 10, 64)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad pipelineID format supplied", errors.AsUserMessage()))
 		return
 	}
 	err = services.CancelPipeline(id)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error cancelling pipeline", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, nil, http.StatusOK)
@@ -189,25 +188,17 @@ func DownloadLogs(c *gin.Context) {
 	pipelineId := c.Param("pipelineId")
 	id, err := strconv.ParseUint(pipelineId, 10, 64)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad pipeline ID format supplied", errors.AsUserMessage()))
 		return
 	}
 	pipeline, err := services.GetPipeline(id)
 	if err != nil {
-		if errors.IsNotFound(err) {
-			shared.ApiOutputError(c, err, http.StatusNotFound)
-		} else {
-			shared.ApiOutputError(c, err, http.StatusInternalServerError)
-		}
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error getting pipeline", errors.AsUserMessage()))
 		return
 	}
 	archive, err := services.GetPipelineLogsArchivePath(pipeline)
 	if err != nil {
-		if goerror.Is(err, os.ErrNotExist) {
-			shared.ApiOutputError(c, err, http.StatusNotFound)
-		} else {
-			shared.ApiOutputError(c, err, http.StatusInternalServerError)
-		}
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error getting logs for pipeline", errors.AsUserMessage()))
 		return
 	}
 	defer os.Remove(archive)
diff --git a/api/plugininfo/plugininifo.go b/api/plugininfo/plugininifo.go
index f5261f1d..471dc495 100644
--- a/api/plugininfo/plugininifo.go
+++ b/api/plugininfo/plugininifo.go
@@ -18,6 +18,7 @@ limitations under the License.
 package plugininfo
 
 import (
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"reflect"
 	"sync"
@@ -172,7 +173,7 @@ func Get(c *gin.Context) {
 	})
 
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error getting plugin info of plugins", errors.AsUserMessage()))
 	}
 
 	shared.ApiOutputSuccess(c, info, http.StatusOK)
diff --git a/api/push/push.go b/api/push/push.go
index 44ecfd08..d24ff875 100644
--- a/api/push/push.go
+++ b/api/push/push.go
@@ -18,6 +18,8 @@ limitations under the License.
 package push
 
 import (
+	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 
 	"github.com/apache/incubator-devlake/api/shared"
@@ -50,12 +52,12 @@ func Post(c *gin.Context) {
 	var rowsToInsert []map[string]interface{}
 	err = c.ShouldBindJSON(&rowsToInsert)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad request body format", errors.AsUserMessage()))
 		return
 	}
 	rowsAffected, err := services.InsertRow(tableName, rowsToInsert)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, fmt.Sprintf("error inserting request body into table %s", tableName), errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, gin.H{"rowsAffected": rowsAffected}, http.StatusOK)
diff --git a/api/router.go b/api/router.go
index 0972c6d2..1b0f6db8 100644
--- a/api/router.go
+++ b/api/router.go
@@ -19,6 +19,7 @@ package api
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"strings"
 
@@ -68,50 +69,53 @@ func RegisterRouter(r *gin.Engine) {
 	for pluginName, apiResources := range pluginsApiResources {
 		for resourcePath, resourceHandlers := range apiResources {
 			for method, h := range resourceHandlers {
-				handler := h // block scoping
 				r.Handle(
 					method,
 					fmt.Sprintf("/plugins/%s/%s", pluginName, resourcePath),
-					func(c *gin.Context) {
-						// connect http request to plugin interface
-						input := &core.ApiResourceInput{}
-						if len(c.Params) > 0 {
-							input.Params = make(map[string]string)
-							for _, param := range c.Params {
-								input.Params[param.Key] = param.Value
-							}
-						}
-						input.Query = c.Request.URL.Query()
-						if c.Request.Body != nil {
-							if strings.HasPrefix(c.Request.Header.Get("Content-Type"), "multipart/form-data;") {
-								input.Request = c.Request
-							} else {
-								err = c.ShouldBindJSON(&input.Body)
-								if err != nil && err.Error() != "EOF" {
-									shared.ApiOutputError(c, err, http.StatusBadRequest)
-									return
-								}
-							}
-						}
-						output, err := handler(input)
-						if err != nil {
-							shared.ApiOutputError(c, err, http.StatusBadRequest)
-						} else if output != nil {
-							status := output.Status
-							if status < http.StatusContinue {
-								status = http.StatusOK
-							}
-							if output.File != nil {
-								c.Data(status, output.File.ContentType, output.File.Data)
-								return
-							}
-							shared.ApiOutputSuccess(c, output.Body, status)
-						} else {
-							shared.ApiOutputSuccess(c, nil, http.StatusOK)
-						}
-					},
+					handlePluginCall(pluginName, h),
 				)
 			}
 		}
 	}
 }
+
+func handlePluginCall(pluginName string, handler core.ApiResourceHandler) func(c *gin.Context) {
+	return func(c *gin.Context) {
+		var err error
+		input := &core.ApiResourceInput{}
+		if len(c.Params) > 0 {
+			input.Params = make(map[string]string)
+			for _, param := range c.Params {
+				input.Params[param.Key] = param.Value
+			}
+		}
+		input.Query = c.Request.URL.Query()
+		if c.Request.Body != nil {
+			if strings.HasPrefix(c.Request.Header.Get("Content-Type"), "multipart/form-data;") {
+				input.Request = c.Request
+			} else {
+				err = c.ShouldBindJSON(&input.Body)
+				if err != nil && err.Error() != "EOF" {
+					shared.ApiOutputError(c, errors.Default.Wrap(err, fmt.Sprintf("could not bind input of plugin %s to JSON", pluginName), errors.AsUserMessage()))
+					return
+				}
+			}
+		}
+		output, err := handler(input)
+		if err != nil {
+			shared.ApiOutputError(c, errors.Default.Wrap(err, fmt.Sprintf("error executing the requested resource for plugin %s", pluginName), errors.AsUserMessage()))
+		} else if output != nil {
+			status := output.Status
+			if status < http.StatusContinue {
+				status = http.StatusOK
+			}
+			if output.File != nil {
+				c.Data(status, output.File.ContentType, output.File.Data)
+				return
+			}
+			shared.ApiOutputSuccess(c, output.Body, status)
+		} else {
+			shared.ApiOutputSuccess(c, nil, http.StatusOK)
+		}
+	}
+}
diff --git a/api/shared/api_output.go b/api/shared/api_output.go
index 871ba804..cecb77ab 100644
--- a/api/shared/api_output.go
+++ b/api/shared/api_output.go
@@ -18,10 +18,12 @@ limitations under the License.
 package shared
 
 import (
+	"fmt"
 	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/logger"
 	"github.com/apache/incubator-devlake/models"
 	"github.com/gin-gonic/gin"
+	"net/http"
 )
 
 type ApiBody struct {
@@ -34,15 +36,17 @@ type ResponsePipelines struct {
 	Pipelines []*models.Pipeline `json:"pipelines"`
 }
 
-func ApiOutputError(c *gin.Context, err error, status int) {
-	if e, ok := err.(*errors.Error); ok {
-		c.JSON(e.Status, &ApiBody{
+// ApiOutputError writes a JSON error message to the HTTP response body
+func ApiOutputError(c *gin.Context, err error) {
+	if e, ok := err.(errors.Error); ok {
+		logger.Global.Error(err, "HTTP %d error", e.GetType().GetHttpCode())
+		c.JSON(e.GetType().GetHttpCode(), &ApiBody{
 			Success: false,
-			Message: err.Error(),
+			Message: e.UserMessage(),
 		})
 	} else {
-		logger.Global.Error("Server Internal Error: %s", err.Error())
-		c.JSON(status, &ApiBody{
+		logger.Global.Error(err, "HTTP %d error (native)", http.StatusInternalServerError)
+		c.JSON(http.StatusInternalServerError, &ApiBody{
 			Success: false,
 			Message: err.Error(),
 		})
@@ -50,6 +54,7 @@ func ApiOutputError(c *gin.Context, err error, status int) {
 	c.Writer.Header().Set("Content-Type", "application/json")
 }
 
+// ApiOutputSuccess writes a JSON success message to the HTTP response body
 func ApiOutputSuccess(c *gin.Context, body interface{}, status int) {
 	if body == nil {
 		body = &ApiBody{
@@ -59,3 +64,14 @@ func ApiOutputSuccess(c *gin.Context, body interface{}, status int) {
 	}
 	c.JSON(status, body)
 }
+
+// ApiOutputAbort writes the HTTP response code header and saves the error internally, but doesn't push it to the response
+func ApiOutputAbort(c *gin.Context, err error) {
+	if e, ok := err.(errors.Error); ok {
+		logger.Global.Error(err, "HTTP %d abort-error", e.GetType().GetHttpCode())
+		_ = c.AbortWithError(e.GetType().GetHttpCode(), fmt.Errorf(e.UserMessage()))
+	} else {
+		logger.Global.Error(err, "HTTP %d abort-error (native)", http.StatusInternalServerError)
+		_ = c.AbortWithError(http.StatusInternalServerError, err)
+	}
+}
diff --git a/api/task/task.go b/api/task/task.go
index 6f218b01..46817545 100644
--- a/api/task/task.go
+++ b/api/task/task.go
@@ -18,6 +18,7 @@ limitations under the License.
 package task
 
 import (
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"strconv"
 
@@ -56,17 +57,17 @@ func Index(c *gin.Context) {
 	var query services.TaskQuery
 	err := c.ShouldBindQuery(&query)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad request body format", errors.AsUserMessage()))
 		return
 	}
 	err = c.ShouldBindUri(&query)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "bad request URI format", errors.AsUserMessage()))
 		return
 	}
 	tasks, count, err := services.GetTasks(&query)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error getting tasks", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, gin.H{"tasks": tasks, "count": count}, http.StatusOK)
@@ -76,12 +77,12 @@ func Delete(c *gin.Context) {
 	taskId := c.Param("taskId")
 	id, err := strconv.ParseUint(taskId, 10, 64)
 	if err != nil {
-		c.JSON(http.StatusBadRequest, "invalid task id")
+		shared.ApiOutputError(c, errors.BadInput.Wrap(err, "invalid task ID format", errors.AsUserMessage()))
 		return
 	}
 	err = services.CancelTask(id)
 	if err != nil {
-		shared.ApiOutputError(c, err, http.StatusBadRequest)
+		shared.ApiOutputError(c, errors.Default.Wrap(err, "error cancelling task", errors.AsUserMessage()))
 		return
 	}
 	shared.ApiOutputSuccess(c, nil, http.StatusOK)
diff --git a/config/config.go b/config/config.go
index 8163342c..4d382887 100644
--- a/config/config.go
+++ b/config/config.go
@@ -18,11 +18,10 @@ limitations under the License.
 package config
 
 import (
-	"errors"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
+	goerror "github.com/cockroachdb/errors"
 	"os"
-	"runtime/debug"
-
 	"path/filepath"
 	"regexp"
 	"strings"
@@ -80,7 +79,7 @@ func replaceNewEnvItemInOldContent(v *viper.Viper, envFileContent string) (strin
 	// prepare reg exp
 	encodeEnvNameReg := regexp.MustCompile(`[^a-zA-Z0-9]`)
 	if encodeEnvNameReg == nil {
-		return ``, fmt.Errorf("encodeEnvNameReg err")
+		return ``, errors.Default.New("encodeEnvNameReg err")
 	}
 
 	for _, key := range v.AllKeys() {
@@ -91,7 +90,7 @@ func replaceNewEnvItemInOldContent(v *viper.Viper, envFileContent string) (strin
 		})
 		envItemReg, err := regexp.Compile(fmt.Sprintf(`(?im)^\s*%v\s*\=.*$`, encodeEnvName))
 		if err != nil {
-			return ``, fmt.Errorf("regexp Compile failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return ``, errors.Default.Wrap(err, "regexp Compile failed")
 		}
 		envFileContent = envItemReg.ReplaceAllStringFunc(envFileContent, func(s string) string {
 			switch ret := val.(type) {
@@ -142,7 +141,7 @@ func WriteConfigAs(v *viper.Viper, filename string) error {
 	flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY
 	configPermissions := os.FileMode(0644)
 	file, err := afero.ReadFile(aferoFile, filename)
-	if err != nil && !errors.Is(err, os.ErrNotExist) {
+	if err != nil && !goerror.Is(err, os.ErrNotExist) {
 		return err
 	}
 
diff --git a/errors/errors.go b/errors/errors.go
index b0a842b9..80fd83a2 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -17,40 +17,34 @@ limitations under the License.
 
 package errors
 
-import (
-	"net/http"
-)
-
-type Error struct {
-	Status  int
-	Message string
-}
-
-func (e *Error) Code() int {
-	return e.Status
-}
-
-func (e *Error) Error() string {
-	return e.Message
-}
-
-func NewError(status int, message string) *Error {
-	return &Error{
-		status,
-		message,
+type (
+	requiredSupertype interface {
+		error
+		Unwrap() error
 	}
-}
-
-func NewNotFound(message string) *Error {
-	return NewError(http.StatusNotFound, message)
-}
+	// Error The interface that all internally managed errors should adhere to.
+	Error interface {
+		requiredSupertype
+		// Message the message associated with this Error.
+		Message() string
+		// UserMessage the message associated with this Error appropriated for end users.
+		UserMessage() string
+		// GetType gets the Type of this error
+		GetType() *Type
+		// As Attempts to cast this Error to the requested Type, and returns nil if it can't.
+		As(*Type) Error
+		// GetData returns the data associated with this Error (may be nil)
+		GetData() interface{}
+	}
+)
 
-func IsNotFound(err error) bool {
-	errCast, ok := err.(*Error)
-	if !ok {
-		return false
+// AsLakeErrorType attempts to cast err to Error, otherwise returns nil
+func AsLakeErrorType(err error) Error {
+	if cast, ok := err.(Error); ok {
+		return cast
 	}
-	return errCast.Status == http.StatusNotFound
+	return nil
 }
 
-var InternalError = NewError(http.StatusInternalServerError, "Server Internal Error")
+var _ error = (Error)(nil)
+var _ requiredSupertype = (Error)(nil)
diff --git a/errors/errors_test.go b/errors/errors_test.go
new file mode 100644
index 00000000..c9a38619
--- /dev/null
+++ b/errors/errors_test.go
@@ -0,0 +1,91 @@
+package errors
+
+import (
+	"errors"
+	"fmt"
+	"github.com/stretchr/testify/require"
+	"os"
+	"strings"
+	"testing"
+)
+
+func TestCrdbErrorImpl(t *testing.T) {
+	err := f1()
+	lakeErr := AsLakeErrorType(err)
+	require.NotNil(t, lakeErr)
+	t.Run("full_error", func(t *testing.T) {
+		fmt.Printf("======================Full Error=======================: \n%v\n\n\n", err)
+		require.Equal(t, err.Error(), lakeErr.Error())
+	})
+	t.Run("raw_message", func(t *testing.T) {
+		msg := lakeErr.Message()
+		require.NotEqual(t, err.Error(), msg)
+		fmt.Printf("======================Raw Message=======================: \n%s\n\n\n", msg)
+		msgParts := strings.Split(msg, "\ncaused by: ")
+		expectedParts := []string{
+			"f1 error (404)",
+			"f2 error [f2 user error] (404)",
+			"f3 error (400)",
+			os.ErrNotExist.Error() + " (400)",
+		}
+		require.Equal(t, expectedParts, msgParts)
+	})
+	t.Run("user_message", func(t *testing.T) {
+		msg := lakeErr.UserMessage()
+		require.NotEqual(t, err.Error(), msg)
+		fmt.Printf("======================User Message=======================: \n%s\n\n\n", msg)
+		msgParts := strings.Split(msg, "\ncaused by: ")
+		expectedParts := []string{
+			"f1 error",
+			"f2 user error",
+		}
+		require.Equal(t, expectedParts, msgParts)
+	})
+	t.Run("type_conversion", func(t *testing.T) {
+		e := lakeErr.As(NotFound)
+		require.Equal(t, NotFound, e.GetType())
+		e = lakeErr.As(BadInput)
+		require.Equal(t, NotFound, e.GetType())
+		e = lakeErr.As(Internal)
+		require.Nil(t, e)
+	})
+	t.Run("type_casting", func(t *testing.T) {
+		require.True(t, errors.Is(lakeErr, os.ErrNotExist))
+	})
+	t.Run("combine_errors_type", func(t *testing.T) {
+		err = Unauthorized.Combine([]error{err, err}, "combined")
+		lakeErr = AsLakeErrorType(err)
+		require.NotNil(t, lakeErr)
+		e := lakeErr.As(Unauthorized)
+		require.Equal(t, Unauthorized, e.GetType())
+		e = lakeErr.As(NotFound)
+		require.Nil(t, e)
+		e = lakeErr.As(BadInput)
+		require.Nil(t, e)
+		require.False(t, errors.Is(lakeErr, os.ErrNotExist))
+	})
+}
+
+func f1() error {
+	err := f2()
+	return Default.Wrap(err, "f1 error", AsUserMessage())
+}
+
+func f2() error {
+	err := f3()
+	return NotFound.Wrap(err, "f2 error", UserMessage("f2 user error"))
+}
+
+func f3() error {
+	err := f4()
+	return Default.Wrap(err, "f3 error")
+}
+
+func f4() error {
+	err := f5()
+	return BadInput.WrapRaw(err)
+}
+
+func f5() error {
+	return os.ErrNotExist
+}
diff --git a/errors/impl.go b/errors/impl.go
new file mode 100644
index 00000000..0f5a6c3a
--- /dev/null
+++ b/errors/impl.go
@@ -0,0 +1,167 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package errors
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+)
+import cerror "github.com/cockroachdb/errors"
+
+type (
+	crdbErrorImpl struct {
+		wrappedRaw error
+		wrapped    *crdbErrorImpl
+		userMsg    string
+		msg        string
+		data       interface{}
+		t          *Type
+	}
+)
+
+var enableStacktraces = false
+
+func init() {
+	enable, exists := os.LookupEnv("ENABLE_STACKTRACE")
+	if !exists {
+		return
+	}
+	enableStacktraces, _ = strconv.ParseBool(enable)
+}
+
+func (e *crdbErrorImpl) Error() string {
+	return fmt.Sprintf("%+v", e.wrappedRaw)
+}
+
+func (e *crdbErrorImpl) Message() string {
+	return strings.Join(e.getMessages(func(err *crdbErrorImpl) string {
+		code := ""
+		if err.t.httpCode != 0 {
+			code = fmt.Sprintf("(%d)", err.t.httpCode)
+		}
+		return err.msg + " " + code
+	}), "\ncaused by: ")
+}
+
+func (e *crdbErrorImpl) UserMessage() string {
+	return strings.Join(e.getMessages(func(err *crdbErrorImpl) string {
+		return err.userMsg
+	}), "\ncaused by: ")
+}
+
+func (e *crdbErrorImpl) Unwrap() error {
+	if e.wrapped != nil {
+		return e.wrapped
+	}
+	return cerror.Cause(e.wrappedRaw)
+}
+
+func (e *crdbErrorImpl) GetType() *Type {
+	return e.t
+}
+
+func (e *crdbErrorImpl) GetData() interface{} {
+	return e.data
+}
+
+func (e *crdbErrorImpl) As(t *Type) Error {
+	err := e
+	for {
+		if err.t == t {
+			return e
+		}
+		lakeErr := AsLakeErrorType(err.Unwrap())
+		if lakeErr == nil {
+			return nil
+		}
+		err = lakeErr.(*crdbErrorImpl)
+	}
+}
+
+func (e *crdbErrorImpl) getMessages(getMessage func(*crdbErrorImpl) string) []string {
+	msgs := []string{}
+	err := e
+	ok := false
+	for {
+		msg := getMessage(err)
+		if msg != "" {
+			msgs = append(msgs, msg)
+		}
+		unwrapped := err.Unwrap()
+		if unwrapped == nil {
+			break
+		}
+		err, ok = unwrapped.(*crdbErrorImpl)
+		if !ok {
+			// don't append the message if the error is "external"
+			break
+		}
+	}
+	return msgs
+}
+
+func newCrdbError(t *Type, err error, message string, opts ...Option) *crdbErrorImpl {
+	cfg := &options{}
+	for _, opt := range opts {
+		opt(cfg)
+	}
+	errType := t
+	var wrappedErr *crdbErrorImpl
+	var wrappedRaw error
+	rawMessage := message
+	if cfg.userMsg != "" {
+		rawMessage = fmt.Sprintf("%s [%s]", message, cfg.userMsg)
+	}
+	if err == nil {
+		if enableStacktraces {
+			wrappedRaw = cerror.NewWithDepth(2, rawMessage)
+		} else {
+			wrappedRaw = errors.New(message)
+		}
+	} else {
+		if cast, ok := err.(*crdbErrorImpl); ok {
+			err = cast.wrappedRaw
+			wrappedErr = cast
+			if t == Default { // inherit wrapped error's type
+				errType = cast.GetType()
+			}
+		}
+		if enableStacktraces {
+			wrappedRaw = cerror.WrapWithDepth(2, err, rawMessage)
+		} else {
+			wrappedRaw = cerror.WithDetail(err, rawMessage)
+		}
+	}
+	impl := &crdbErrorImpl{
+		wrappedRaw: wrappedRaw,
+		wrapped:    wrappedErr,
+		msg:        rawMessage,
+		userMsg:    cfg.userMsg,
+		data:       cfg.data,
+		t:          errType,
+	}
+	if cfg.asUserMsg {
+		impl.userMsg = message // set to original
+	}
+	return impl
+}
+
+var _ Error = (*crdbErrorImpl)(nil)
diff --git a/errors/sub_task_error.go b/errors/sub_task_error.go
deleted file mode 100644
index 96faebc8..00000000
--- a/errors/sub_task_error.go
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You under the Apache License, Version 2.0
-(the "License"); you may not use this file except in compliance with
-the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package errors
-
-var _ error = (*SubTaskError)(nil)
-
-type SubTaskError struct {
-	SubTaskName string
-	Message     string
-}
-
-func (e *SubTaskError) Error() string {
-	return e.Message
-}
-
-func (e *SubTaskError) GetSubTaskName() string {
-	return e.SubTaskName
-}
diff --git a/errors/types.go b/errors/types.go
new file mode 100644
index 00000000..5a514f37
--- /dev/null
+++ b/errors/types.go
@@ -0,0 +1,143 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package errors
+
+import (
+	"fmt"
+	"net/http"
+	"strings"
+)
+
+// Supported error types
+var (
+	Default      = register(nil)
+	SubtaskErr   = register(&Type{meta: "subtask"})
+	NotFound     = register(&Type{httpCode: http.StatusNotFound, meta: "not-found"})
+	BadInput     = register(&Type{httpCode: http.StatusBadRequest, meta: "bad-input"})
+	Unauthorized = register(&Type{httpCode: http.StatusUnauthorized, meta: "unauthorized"})
+	Forbidden    = register(&Type{httpCode: http.StatusForbidden, meta: "forbidden"})
+	Internal     = register(&Type{httpCode: http.StatusInternalServerError, meta: "internal"})
+	Timeout      = register(&Type{httpCode: http.StatusGatewayTimeout, meta: "timeout"})
+
+	//cached values
+	typesByHttpCode = map[int]*Type{}
+)
+
+type (
+	// Type error are constructed from these, and they contain metadata about them.
+	Type struct {
+		meta string
+		// below are optional fields
+		httpCode int
+	}
+
+	// Option add customized properties to the Error
+	Option func(*options)
+
+	options struct {
+		userMsg   string
+		asUserMsg bool
+		data      interface{}
+	}
+)
+
+func HttpStatus(code int) *Type {
+	t, ok := typesByHttpCode[code]
+	if !ok {
+		t = Internal
+	}
+	return t
+}
+
+// New constructs a new Error instance with this message
+func (t *Type) New(message string, opts ...Option) Error {
+	return newCrdbError(t, nil, message, opts...)
+}
+
+// Wrap constructs a new Error instance with this message and wraps the passed in error
+func (t *Type) Wrap(err error, message string, opts ...Option) Error {
+	return newCrdbError(t, err, message, opts...)
+}
+
+// WrapRaw constructs a new Error instance that directly wraps this error with no additional context
+func (t *Type) WrapRaw(err error) Error {
+	msg := ""
+	lakeErr := AsLakeErrorType(err)
+	if lakeErr != nil {
+		msg = "" // there's nothing new to add
+	} else {
+		msg = err.Error()
+	}
+	return newCrdbError(t, err, msg)
+}
+
+// Combine constructs a new Error from combining multiple errors. Stacktrace info for each of the errors will not be present in the result.
+func (t *Type) Combine(errs []error, msg string, opts ...Option) Error {
+	msgs := []string{}
+	for _, e := range errs {
+		if le := AsLakeErrorType(e); le != nil {
+			if msg0 := le.Message(); msg0 != "" {
+				msgs = append(msgs, le.Message())
+			}
+		} else {
+			msgs = append(msgs, e.Error())
+		}
+	}
+	effectiveMsg := strings.Join(msgs, "\n=====================\n")
+	effectiveMsg = "\t" + strings.ReplaceAll(effectiveMsg, "\n", "\n\t")
+	return newCrdbError(t, nil, fmt.Sprintf("%s\ncombined messages: \n{\n%s\n}", msg, effectiveMsg), opts...)
+}
+
+// GetHttpCode gets the associated Http code with this Type, if explicitly set, otherwise http.StatusInternalServerError
+func (t *Type) GetHttpCode() int {
+	if t.httpCode == 0 {
+		return http.StatusInternalServerError
+	}
+	return t.httpCode
+}
+
+// UserMessage add a user-friendly message to the Error
+func UserMessage(msg string) Option {
+	return func(opts *options) {
+		opts.userMsg = msg
+	}
+}
+
+// AsUserMessage use the ordinary message as the user-friendly message of the Error
+func AsUserMessage() Option {
+	return func(opts *options) {
+		opts.asUserMsg = true
+	}
+}
+
+// WithData associate data with this Error
+func WithData(data interface{}) Option {
+	return func(opts *options) {
+		opts.data = data
+	}
+}
+
+func register(t *Type) *Type {
+	if t == nil {
+		t = &Type{meta: "default"}
+		typesByHttpCode[t.httpCode] = t
+	} else if t.httpCode != 0 {
+		typesByHttpCode[t.httpCode] = t
+	}
+	return t
+}
diff --git a/generator/cmd/create_collector.go b/generator/cmd/create_collector.go
index bef7385f..dce1c67d 100644
--- a/generator/cmd/create_collector.go
+++ b/generator/cmd/create_collector.go
@@ -18,8 +18,8 @@ limitations under the License.
 package cmd
 
 import (
-	"errors"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/generator/util"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
@@ -37,11 +37,11 @@ func init() {
 func collectorNameNotExistValidateHoc(pluginName string) func(input string) error {
 	collectorNameValidate := func(input string) error {
 		if input == `` {
-			return errors.New("please input which data would you will collect (snake_format)")
+			return errors.Default.New("please input which data would you will collect (snake_format)")
 		}
 		snakeNameReg := regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`)
 		if !snakeNameReg.MatchString(input) {
-			return errors.New("collector name invalid (start with a-z and consist with a-z0-9_)")
+			return errors.Default.New("collector name invalid (start with a-z and consist with a-z0-9_)")
 		}
 		_, err := os.Stat(filepath.Join(`plugins`, pluginName, `tasks`, input+`_collector.go`))
 		if os.IsNotExist(err) {
@@ -50,7 +50,7 @@ func collectorNameNotExistValidateHoc(pluginName string) func(input string) erro
 		if err != nil {
 			return err
 		}
-		return errors.New("collector exists")
+		return errors.Default.New("collector exists")
 	}
 	return collectorNameValidate
 }
@@ -58,7 +58,7 @@ func collectorNameNotExistValidateHoc(pluginName string) func(input string) erro
 func collectorNameExistValidateHoc(pluginName string) func(input string) error {
 	collectorNameValidate := func(input string) error {
 		if input == `` {
-			return errors.New("please input which data would you will collect (snake_format)")
+			return errors.Default.New("please input which data would you will collect (snake_format)")
 		}
 		_, err := os.Stat(filepath.Join(`plugins`, pluginName, `tasks`, input+`_collector.go`))
 		return err
@@ -105,10 +105,10 @@ Type in what the name of collector is, then generator will create a new collecto
 			Label: "http_path",
 			Validate: func(input string) error {
 				if input == `` {
-					return errors.New("http_path require")
+					return errors.BadInput.New("http_path required", errors.AsUserMessage())
 				}
 				if strings.HasPrefix(input, `/`) {
-					return errors.New("http_path shouldn't start with '/'")
+					return errors.BadInput.New("http_path shouldn't start with '/'", errors.AsUserMessage())
 				}
 				return nil
 			},
diff --git a/generator/cmd/create_extractor.go b/generator/cmd/create_extractor.go
index d16c3d7f..42cc5eb5 100644
--- a/generator/cmd/create_extractor.go
+++ b/generator/cmd/create_extractor.go
@@ -18,8 +18,8 @@ limitations under the License.
 package cmd
 
 import (
-	"errors"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/generator/util"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
@@ -37,11 +37,11 @@ func init() {
 func extractorNameNotExistValidateHoc(pluginName string) func(input string) error {
 	extractorNameValidate := func(input string) error {
 		if input == `` {
-			return errors.New("please input which data would you will extract (snake_format)")
+			return errors.Default.New("please input which data would you will extract (snake_format)")
 		}
 		snakeNameReg := regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`)
 		if !snakeNameReg.MatchString(input) {
-			return errors.New("extractor name invalid (start with a-z and consist with a-z0-9_)")
+			return errors.Default.New("extractor name invalid (start with a-z and consist with a-z0-9_)")
 		}
 		_, err := os.Stat(filepath.Join(`plugins`, pluginName, `tasks`, input+`_extractor.go`))
 		if os.IsNotExist(err) {
@@ -50,7 +50,7 @@ func extractorNameNotExistValidateHoc(pluginName string) func(input string) erro
 		if err != nil {
 			return err
 		}
-		return errors.New("extractor exists")
+		return errors.Default.New("extractor exists")
 	}
 	return extractorNameValidate
 }
diff --git a/generator/cmd/create_migration.go b/generator/cmd/create_migration.go
index 89acc852..7e101fd5 100644
--- a/generator/cmd/create_migration.go
+++ b/generator/cmd/create_migration.go
@@ -18,8 +18,8 @@ limitations under the License.
 package cmd
 
 import (
-	"errors"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -71,7 +71,7 @@ If framework passed, generator will create a new migration in models/migrationsc
 		}
 		_, err = os.Stat(migrationPath)
 		if os.IsNotExist(err) {
-			cobra.CheckErr(errors.New(`migrationscripts not init. please run init-migration first`))
+			cobra.CheckErr(errors.Default.New(`migrationscripts not init. please run init-migration first`))
 		}
 		cobra.CheckErr(err)
 
@@ -122,11 +122,11 @@ If framework passed, generator will create a new migration in models/migrationsc
 
 func purposeNotExistValidate(input string) error {
 	if input == `` {
-		return errors.New("purpose require")
+		return errors.Default.New("purpose require")
 	}
 	camelNameReg := regexp.MustCompile(`^[a-z][A-Za-z0-9]*$`)
 	if !camelNameReg.MatchString(input) {
-		return errors.New("purpose invalid (please use camelCase format, start with a-z and consist with a-zA-Z0-9)")
+		return errors.Default.New("purpose invalid (please use camelCase format, start with a-z and consist with a-zA-Z0-9)")
 	}
 
 	return nil
diff --git a/generator/cmd/create_plugin.go b/generator/cmd/create_plugin.go
index 23b9d02b..1eaf8e06 100644
--- a/generator/cmd/create_plugin.go
+++ b/generator/cmd/create_plugin.go
@@ -18,8 +18,8 @@ limitations under the License.
 package cmd
 
 import (
-	"errors"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/generator/util"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
@@ -36,14 +36,14 @@ func init() {
 
 func pluginNameNotExistValidate(input string) error {
 	if input == `` {
-		return errors.New("plugin name require")
+		return errors.Default.New("plugin name require")
 	}
 	snakeNameReg := regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`)
 	if !snakeNameReg.MatchString(input) {
-		return errors.New("plugin name invalid (start with a-z and consist with a-z0-9_)")
+		return errors.Default.New("plugin name invalid (start with a-z and consist with a-z0-9_)")
 	}
 	if strings.ToLower(input) == `framework` || strings.ToLower(input) == `core` || strings.ToLower(input) == `helper` {
-		return errors.New("plugin name cannot be `framework` or `core` or `helper`")
+		return errors.Default.New("plugin name cannot be `framework` or `core` or `helper`")
 	}
 	_, err := os.Stat(`plugins/` + input)
 	if os.IsNotExist(err) {
@@ -52,12 +52,12 @@ func pluginNameNotExistValidate(input string) error {
 	if err != nil {
 		return err
 	}
-	return errors.New("plugin exists")
+	return errors.Default.New("plugin exists")
 }
 
 func pluginNameExistValidate(input string) error {
 	if input == `` {
-		return errors.New("plugin name require")
+		return errors.Default.New("plugin name require")
 	}
 	_, err := os.Stat(`plugins/` + input)
 	return err
diff --git a/generator/cmd/e2e_raw_create.go b/generator/cmd/e2e_raw_create.go
index f66eb2d6..862d69c0 100644
--- a/generator/cmd/e2e_raw_create.go
+++ b/generator/cmd/e2e_raw_create.go
@@ -18,7 +18,7 @@ limitations under the License.
 package cmd
 
 import (
-	"errors"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/helpers/e2ehelper"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
@@ -64,10 +64,10 @@ Type in what the raw_table is, then generator will export and save in plugins/$p
 				Default: `_raw_`,
 				Validate: func(input string) error {
 					if input == `` {
-						return errors.New("raw_table_name require")
+						return errors.Default.New("raw_table_name require")
 					}
 					if !strings.HasPrefix(input, `_raw_`) {
-						return errors.New("raw_table_name should start with `_raw_`")
+						return errors.Default.New("raw_table_name should start with `_raw_`")
 					}
 					return nil
 				},
@@ -82,10 +82,10 @@ Type in what the raw_table is, then generator will export and save in plugins/$p
 			Default: rawTableName + `.csv`,
 			Validate: func(input string) error {
 				if input == `` {
-					return errors.New("csv_file_name require")
+					return errors.Default.New("csv_file_name require")
 				}
 				if !strings.HasSuffix(input, `.csv`) {
-					return errors.New("csv_file_name should end with `.csv`")
+					return errors.Default.New("csv_file_name should end with `.csv`")
 				}
 				return nil
 			},
diff --git a/generator/cmd/init_migration.go b/generator/cmd/init_migration.go
index bed53ce2..1c4c911e 100644
--- a/generator/cmd/init_migration.go
+++ b/generator/cmd/init_migration.go
@@ -18,8 +18,8 @@ limitations under the License.
 package cmd
 
 import (
-	"errors"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"os"
 	"path/filepath"
 	"time"
@@ -59,7 +59,7 @@ Type in which plugin do you want init migrations in, then generator will create
 		migrationPath := filepath.Join(`plugins`, pluginName, `models`, `migrationscripts`)
 		_, err := os.Stat(migrationPath)
 		if !os.IsNotExist(err) {
-			cobra.CheckErr(errors.New(`migrationscripts inited or path read file`))
+			cobra.CheckErr(errors.Default.New(`migrationscripts inited or path read file`))
 		}
 
 		// create vars
diff --git a/go.mod b/go.mod
index e2481d9a..c99ecc60 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/apache/incubator-devlake
 go 1.19
 
 require (
+	github.com/cockroachdb/errors v1.9.0
 	github.com/gin-contrib/cors v1.3.1
 	github.com/gin-gonic/gin v1.7.7
 	github.com/go-git/go-git/v5 v5.4.2
@@ -43,12 +44,15 @@ require (
 	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
+	github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
+	github.com/cockroachdb/redact v1.1.3 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/denisenkom/go-mssqldb v0.10.0 // indirect
 	github.com/emirpasic/gods v1.12.0 // indirect
 	github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
 	github.com/fsnotify/fsnotify v1.5.1 // indirect
+	github.com/getsentry/sentry-go v0.12.0 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
 	github.com/go-errors/errors v1.4.2 // indirect
 	github.com/go-git/gcfg v1.5.0 // indirect
@@ -84,22 +88,24 @@ require (
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.11 // indirect
 	github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
+	github.com/kr/pretty v0.3.0 // indirect
+	github.com/kr/text v0.2.0 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
-	github.com/mattn/go-colorable v0.1.6 // indirect
-	github.com/mattn/go-isatty v0.0.13 // indirect
+	github.com/mattn/go-colorable v0.1.11 // indirect
+	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/mattn/go-sqlite3 v1.14.6 // indirect
 	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.1 // indirect
-	github.com/onsi/ginkgo v1.13.0 // indirect
 	github.com/onsi/gomega v1.10.3 // indirect
 	github.com/pborman/uuid v1.2.1 // indirect
 	github.com/pelletier/go-toml v1.9.3 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/robfig/cron v1.2.0 // indirect
+	github.com/rogpeppe/go-internal v1.8.1 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sergi/go-diff v1.1.0 // indirect
 	github.com/spf13/cast v1.4.1 // indirect
diff --git a/go.sum b/go.sum
index 6dc810da..dff4852e 100644
--- a/go.sum
+++ b/go.sum
@@ -37,8 +37,15 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
+github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
+github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
+github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
+github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
+github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
 github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
 github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
 github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
@@ -51,17 +58,21 @@ github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C6
 github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
 github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
 github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
 github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
+github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -82,10 +93,27 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/cockroachdb/datadriven v1.0.1-0.20211007161720-b558070c3be0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
+github.com/cockroachdb/datadriven v1.0.1-0.20220214170620-9913f5bc19b7/go.mod h1:hi0MtSY3AYDQNDi83kDkMH5/yqM/CsIrsOITkSoH7KI=
+github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
+github.com/cockroachdb/errors v1.8.8/go.mod h1:z6VnEL3hZ/2ONZEvG7S5Ym0bU2AqPcEKnIiA1wbsSu0=
+github.com/cockroachdb/errors v1.9.0 h1:B48dYem5SlAY7iU8AKsgedb4gH6mo+bDkbtLIvM/a88=
+github.com/cockroachdb/errors v1.9.0/go.mod h1:vaNcEYYqbIqB5JhKBhFV9CneUqeuEbB2OYJBK4GBNYQ=
+github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
+github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74=
+github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
+github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
+github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
+github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=
+github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -97,6 +125,11 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
 github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8=
 github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
 github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -107,27 +140,38 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
 github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
+github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
+github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
 github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
+github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
+github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk=
+github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
 github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
 github.com/gin-contrib/gzip v0.0.5 h1:mhnVU32YnnBh2LPH2iqRqsA/eR7SAqRaD388jL2s/j0=
 github.com/gin-contrib/gzip v0.0.5/go.mod h1:OPIK6HR0Um2vNmBUTlayD7qle4yVVRZT0PyhdUigrKk=
+github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
 github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
 github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
 github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
 github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
 github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
 github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
 github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
 github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
 github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
@@ -146,6 +190,7 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
 github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -176,6 +221,9 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
 github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
 github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
+github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
 github.com/gocarina/gocsv v0.0.0-20220707092902-b9da1f06c77e h1:GMIV+S6grz+vlIaUsP+fedQ6L+FovyMPMY26WO8dwQE=
 github.com/gocarina/gocsv v0.0.0-20220707092902-b9da1f06c77e/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -186,10 +234,12 @@ github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q8
 github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
 github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA=
 github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
+github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -224,6 +274,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
 github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
 github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -239,6 +290,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -266,6 +318,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
 github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
@@ -281,6 +335,7 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
 github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
 github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -291,12 +346,21 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
+github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
 github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
+github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
+github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
+github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
+github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
+github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
 github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
 github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
 github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@@ -370,6 +434,7 @@ github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
 github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
@@ -378,10 +443,28 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
+github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
+github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
+github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
+github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
+github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
+github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
+github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
+github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
+github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
+github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
+github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
+github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
 github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
 github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
@@ -395,6 +478,9 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
+github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
+github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
 github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
@@ -407,6 +493,7 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
 github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/libgit2/git2go/v33 v33.0.6 h1:F//bA3/pgSTVq2hLNahhnof9NxyCzFF/c3MB6lb93Qo=
 github.com/libgit2/git2go/v33 v33.0.6/go.mod h1:KdpqkU+6+++4oHna/MIOgx4GCQ92IPCdpVRMRI80J+4=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
 github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -421,23 +508,30 @@ github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlW
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
 github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
+github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
-github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
 github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
 github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
+github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
+github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
+github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
 github.com/merico-dev/graphql v0.0.0-20220804061427-a2245fa66df2 h1:sOXuZIg3OwBnvJFfIuO8wegiLpeDCOSVvk2dsbjurd8=
 github.com/merico-dev/graphql v0.0.0-20220804061427-a2245fa66df2/go.mod h1:dcDqG8HXVtfEhTCipFMa0Q+RTKTtDKIO2vJt+JVzHEQ=
 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
+github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -456,10 +550,18 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y=
 github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
@@ -478,8 +580,11 @@ github.com/panjf2000/ants/v2 v2.4.6/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OI
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
 github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
 github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
+github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -496,18 +601,23 @@ github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
+github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
 github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
@@ -524,18 +634,24 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
 github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
 github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
 github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/pflag v1.0.6-0.20200504143853-81378bbcd8a1 h1:zrNp7OPtn2fjeNHI9CghvwxqQvvkK0RxUo86hE86vhU=
 github.com/spf13/pflag v1.0.6-0.20200504143853-81378bbcd8a1/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
 github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
 github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
@@ -560,18 +676,34 @@ github.com/swaggo/gin-swagger v1.4.3/go.mod h1:hBg6tGeKJsUu/P79BH+WGUR8nq2LuGE0O
 github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
 github.com/swaggo/swag v1.8.3 h1:3pZSSCQ//gAH88lfmxM3Cd1+JCsxV8Md6f36b9hrZ5s=
 github.com/swaggo/swag v1.8.3/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
 github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
+github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
+github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
+github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 github.com/viant/afs v1.16.0 h1:yb9TQ1gjVVLji9lcXLWaarklqmGWeXTZOwc2fwJevCI=
 github.com/viant/afs v1.16.0/go.mod h1:wdiEDffZKJwj1ZSFasy7hHoxLQdSpFZkd3XOWNt1aN0=
 github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
 github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
 github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
 github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
+github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
+github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
+github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -611,15 +743,18 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
 go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
@@ -670,9 +805,11 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -681,6 +818,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -708,6 +846,7 @@ golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5o
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 h1:UreQrH7DbFXSi9ZFox6FNT3WBooWmdANpU+IfkT1T4I=
@@ -743,6 +882,7 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -753,6 +893,7 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -802,7 +943,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220222200937-f2425489ef4c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -823,14 +967,18 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/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-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -882,6 +1030,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
 golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
 golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
@@ -965,6 +1114,7 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
 google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
 google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf h1:SVYXkUz2yZS9FWb2Gm8ivSlbNQzL2Z/NpPKE3RG2jWk=
 google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
 google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
@@ -1013,10 +1163,13 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
 gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
 gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
+gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
 gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
@@ -1029,6 +1182,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/helpers/e2ehelper/data_flow_tester.go b/helpers/e2ehelper/data_flow_tester.go
index 60a30f81..710491c1 100644
--- a/helpers/e2ehelper/data_flow_tester.go
+++ b/helpers/e2ehelper/data_flow_tester.go
@@ -22,6 +22,7 @@ import (
 	"database/sql"
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"os"
 	"strconv"
 	"strings"
@@ -57,13 +58,13 @@ import (
 // Recommended Usage:
 
 // DataFlowTester use `N`
-//   1. Create a folder under your plugin root folder. i.e. `plugins/gitlab/e2e/ to host all your e2e-tests`
-//   2. Create a folder named `tables` to hold all data in `csv` format
-//   3. Create e2e test-cases to cover all possible data-flow routes
+//  1. Create a folder under your plugin root folder. i.e. `plugins/gitlab/e2e/ to host all your e2e-tests`
+//  2. Create a folder named `tables` to hold all data in `csv` format
+//  3. Create e2e test-cases to cover all possible data-flow routes
 //
 // Example code:
 //
-//   See [Gitlab Project Data Flow Test](plugins/gitlab/e2e/project_test.go) for detail
+//	See [Gitlab Project Data Flow Test](plugins/gitlab/e2e/project_test.go) for detail
 type DataFlowTester struct {
 	Cfg    *viper.Viper
 	Db     *gorm.DB
@@ -96,7 +97,7 @@ func NewDataFlowTester(t *testing.T, pluginName string, pluginMeta core.PluginMe
 	cfg := config.GetConfig()
 	e2eDbUrl := cfg.GetString(`E2E_DB_URL`)
 	if e2eDbUrl == `` {
-		panic(fmt.Errorf(`e2e can only run with E2E_DB_URL, please set it in .env`))
+		panic(errors.Default.New(`e2e can only run with E2E_DB_URL, please set it in .env`))
 	}
 	cfg.Set(`DB_URL`, cfg.GetString(`E2E_DB_URL`))
 	db, err := runner.NewGormDb(cfg, logger.Global)
@@ -228,12 +229,12 @@ func (t *DataFlowTester) CreateSnapshot(dst schema.Tabler, opts TableOptions) {
 		dal.Orderby(strings.Join(pkColumnNames, `,`)),
 	)
 	if err != nil {
-		panic(fmt.Errorf("unable to run select query on table %s: %v", dst.TableName(), err))
+		panic(errors.Default.Wrap(err, fmt.Sprintf("unable to run select query on table %s", dst.TableName())))
 	}
 
 	columns, err := dbCursor.Columns()
 	if err != nil {
-		panic(fmt.Errorf("unable to get columns from table %s: %v", dst.TableName(), err))
+		panic(errors.Default.Wrap(err, fmt.Sprintf("unable to get columns from table %s", dst.TableName())))
 	}
 	csvWriter := pluginhelper.NewCsvFileWriter(opts.CSVRelPath, columns)
 	defer csvWriter.Close()
@@ -256,7 +257,7 @@ func (t *DataFlowTester) CreateSnapshot(dst schema.Tabler, opts TableOptions) {
 	for dbCursor.Next() {
 		err = dbCursor.Scan(forScanValues...)
 		if err != nil {
-			panic(fmt.Errorf("unable to scan row on table %s: %v", dst.TableName(), err))
+			panic(errors.Default.Wrap(err, fmt.Sprintf("unable to scan row on table %s: %v", dst.TableName(), err)))
 		}
 		values := make([]string, len(allFields))
 		for i := range forScanValues {
diff --git a/helpers/pluginhelper/csv_file_iterator.go b/helpers/pluginhelper/csv_file_iterator.go
index 2de777e0..33cac851 100644
--- a/helpers/pluginhelper/csv_file_iterator.go
+++ b/helpers/pluginhelper/csv_file_iterator.go
@@ -28,9 +28,8 @@ import (
 //
 // Example CSV format (exported by dbeaver):
 //
-//   "id","name","json","created_at"
-//   123,"foobar","{""url"": ""https://example.com""}","2022-05-05 09:56:43.438000000"
-//
+//	"id","name","json","created_at"
+//	123,"foobar","{""url"": ""https://example.com""}","2022-05-05 09:56:43.438000000"
 type CsvFileIterator struct {
 	file   *os.File
 	reader *csv.Reader
diff --git a/helpers/pluginhelper/csv_file_writer.go b/helpers/pluginhelper/csv_file_writer.go
index c413c5ad..4241a3cd 100644
--- a/helpers/pluginhelper/csv_file_writer.go
+++ b/helpers/pluginhelper/csv_file_writer.go
@@ -26,9 +26,8 @@ import (
 //
 // Example CSV format (exported by dbeaver):
 //
-//   "id","name","json","created_at"
-//   123,"foobar","{""url"": ""https://example.com""}","2022-05-05 09:56:43.438000000"
-//
+//	"id","name","json","created_at"
+//	123,"foobar","{""url"": ""https://example.com""}","2022-05-05 09:56:43.438000000"
 type CsvFileWriter struct {
 	file   *os.File
 	writer *csv.Writer
diff --git a/logger/logger.go b/logger/logger.go
index c4a52aac..15ca4325 100644
--- a/logger/logger.go
+++ b/logger/logger.go
@@ -69,12 +69,12 @@ func (l *DefaultLogger) Info(format string, a ...interface{}) {
 	l.Log(core.LOG_INFO, format, a...)
 }
 
-func (l *DefaultLogger) Warn(format string, a ...interface{}) {
-	l.Log(core.LOG_WARN, format, a...)
+func (l *DefaultLogger) Warn(err error, format string, a ...interface{}) {
+	l.Log(core.LOG_WARN, formatMessage(err, format, a...))
 }
 
-func (l *DefaultLogger) Error(format string, a ...interface{}) {
-	l.Log(core.LOG_ERROR, format, a...)
+func (l *DefaultLogger) Error(err error, format string, a ...interface{}) {
+	l.Log(core.LOG_ERROR, formatMessage(err, format, a...))
 }
 
 func (l *DefaultLogger) SetStream(config *core.LoggerStreamConfig) {
@@ -100,7 +100,7 @@ func (l *DefaultLogger) Nested(newPrefix string) core.Logger {
 	}
 	newLogger, err := l.getLogger(newTotalPrefix)
 	if err != nil {
-		l.Error("error getting a new logger: %v", newLogger)
+		l.Error(err, "error getting a new logger")
 		return l
 	}
 	return newLogger
@@ -130,4 +130,15 @@ func (l *DefaultLogger) createPrefix(newPrefix string) string {
 	return fmt.Sprintf("%s [%s]", l.config.Prefix, newPrefix)
 }
 
+func formatMessage(err error, msg string, args ...interface{}) string {
+	if err == nil {
+		return fmt.Sprintf(msg, args...)
+	}
+	formattedErr := strings.ReplaceAll(err.Error(), "\n", "\n\t")
+	if msg == "" {
+		return formattedErr
+	}
+	return fmt.Sprintf("%s\n%s", fmt.Sprintf(msg, args...), formattedErr)
+}
+
 var _ core.Logger = (*DefaultLogger)(nil)
diff --git a/models/domainlayer/didgen/domain_id_generator.go b/models/domainlayer/didgen/domain_id_generator.go
index 406f1a3c..2c1ff94c 100644
--- a/models/domainlayer/didgen/domain_id_generator.go
+++ b/models/domainlayer/didgen/domain_id_generator.go
@@ -19,6 +19,7 @@ package didgen
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 
 	"github.com/apache/incubator-devlake/impl/dalgorm"
@@ -55,7 +56,7 @@ func NewDomainIdGenerator(entityPtr interface{}) *DomainIdGenerator {
 	pk := dal.GetPrimaryKeyFields(t)
 
 	if len(pk) == 0 {
-		panic(fmt.Errorf("no primary key found for %s:%s", pluginName, structName))
+		panic(errors.Default.New(fmt.Sprintf("no primary key found for %s:%s", pluginName, structName)))
 	}
 
 	return &DomainIdGenerator{
@@ -74,11 +75,11 @@ func (g *DomainIdGenerator) Generate(pkValues ...interface{}) string {
 		if pkValueType == wildcardType {
 			break
 		} else if pkValueType != g.pk[i].Type {
-			panic(fmt.Errorf("primary key type does not match: %s is %s type, and it should be %s type",
+			panic(errors.Default.New(fmt.Sprintf("primary key type does not match: %s is %s type, and it should be %s type",
 				g.pk[i].Name,
 				pkValueType.Name(),
 				g.pk[i].Type.Name(),
-			))
+			)))
 		}
 	}
 	return id
diff --git a/plugins/ae/api/connection.go b/plugins/ae/api/connection.go
index 0350bcaf..1639ac13 100644
--- a/plugins/ae/api/connection.go
+++ b/plugins/ae/api/connection.go
@@ -20,6 +20,7 @@ package api
 import (
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"time"
 
@@ -47,12 +48,12 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	var connection models.TestConnectionRequest
 	err = mapstructure.Decode(input.Body, &connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	// validate
 	err = vld.Struct(connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not validate request parameters", errors.AsUserMessage())
 	}
 
 	// load and process cconfiguration
diff --git a/plugins/ae/impl/impl.go b/plugins/ae/impl/impl.go
index 54bbee8f..6b6dffee 100644
--- a/plugins/ae/impl/impl.go
+++ b/plugins/ae/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/apache/incubator-devlake/migration"
 	"github.com/apache/incubator-devlake/plugins/ae/api"
@@ -75,7 +76,7 @@ func (plugin AE) PrepareTaskData(taskCtx core.TaskContext, options map[string]in
 		return nil, err
 	}
 	if op.ProjectId <= 0 {
-		return nil, fmt.Errorf("projectId is required")
+		return nil, errors.Default.New("projectId is required")
 	}
 
 	connection := &models.AeConnection{}
@@ -131,7 +132,7 @@ func (plugin AE) ApiResources() map[string]map[string]core.ApiResourceHandler {
 func (plugin AE) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.AeTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/ae/tasks/commits_extractor.go b/plugins/ae/tasks/commits_extractor.go
index 403fd422..19ac0d38 100644
--- a/plugins/ae/tasks/commits_extractor.go
+++ b/plugins/ae/tasks/commits_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/ae/models"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/azure/api/blueprint.go b/plugins/azure/api/blueprint.go
index 93f028ff..789776b4 100644
--- a/plugins/azure/api/blueprint.go
+++ b/plugins/azure/api/blueprint.go
@@ -19,7 +19,6 @@ package api
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jenkins/tasks"
diff --git a/plugins/azure/api/connection.go b/plugins/azure/api/connection.go
index b54ef456..ec6fde18 100644
--- a/plugins/azure/api/connection.go
+++ b/plugins/azure/api/connection.go
@@ -20,6 +20,7 @@ package api
 import (
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"time"
 
@@ -37,12 +38,12 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	var connection models.TestConnectionRequest
 	err = mapstructure.Decode(input.Body, &connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	// validate
 	err = vld.Struct(connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not validate request parameters", errors.AsUserMessage())
 	}
 	// test connection
 	encodedToken := utils.GetEncodedToken(connection.Username, connection.Password)
@@ -66,19 +67,20 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	}
 
 	if res.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+		return nil, errors.HttpStatus(res.StatusCode).New("unexpected status code while testing connection")
 	}
 	return nil, nil
 }
 
 /*
 POST /plugins/zaure/connections
-{
-	"name": "zaure data connection name",
-	"endpoint": "zaure api endpoint, i.e. https://ci.zaure.io/",
-	"username": "username, usually should be email address",
-	"password": "zaure api access token"
-}
+
+	{
+		"name": "zaure data connection name",
+		"endpoint": "zaure api endpoint, i.e. https://ci.zaure.io/",
+		"username": "username, usually should be email address",
+		"password": "zaure api access token"
+	}
 */
 func PostConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
 	// create a new connection
diff --git a/plugins/azure/impl/impl.go b/plugins/azure/impl/impl.go
index f0d0888c..c832ad01 100644
--- a/plugins/azure/impl/impl.go
+++ b/plugins/azure/impl/impl.go
@@ -2,6 +2,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/migration"
 	"github.com/apache/incubator-devlake/plugins/azure/api"
 	"github.com/apache/incubator-devlake/plugins/azure/models"
@@ -50,7 +51,7 @@ func (plugin Azure) PrepareTaskData(taskCtx core.TaskContext, options map[string
 		return nil, err
 	}
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.Default.New("connectionId is invalid")
 	}
 
 	connection := &models.AzureConnection{}
@@ -106,7 +107,7 @@ func (plugin Azure) MigrationScripts() []migration.Script {
 func (plugin Azure) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.AzureTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/azure/tasks/api_client.go b/plugins/azure/tasks/api_client.go
index d25693af..9e7cbf2f 100644
--- a/plugins/azure/tasks/api_client.go
+++ b/plugins/azure/tasks/api_client.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/azure/models"
 	"net/http"
 
@@ -39,7 +40,7 @@ func CreateApiClient(taskCtx core.TaskContext, connection *models.AzureConnectio
 
 	apiClient.SetAfterFunction(func(res *http.Response) error {
 		if res.StatusCode == http.StatusUnauthorized {
-			return fmt.Errorf("authentication failed, please check your Username/Password")
+			return errors.Unauthorized.New("authentication failed, please check your Username/Password")
 		}
 		return nil
 	})
diff --git a/plugins/azure/tasks/repo_extractor.go b/plugins/azure/tasks/repo_extractor.go
index 391c2258..74be202f 100644
--- a/plugins/azure/tasks/repo_extractor.go
+++ b/plugins/azure/tasks/repo_extractor.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/plugins/azure/models"
@@ -83,7 +84,7 @@ func ExtractApiRepositories(taskCtx core.SubTaskContext) error {
 				return nil, err
 			}
 			if body.ID == "" {
-				return nil, fmt.Errorf("repo %s not found", data.Options.Project)
+				return nil, errors.Default.New(fmt.Sprintf("repo %s not found", data.Options.Project))
 			}
 			results := make([]interface{}, 0, 1)
 			azureRepository := &models.AzureRepo{
diff --git a/plugins/azure/tasks/task_data.go b/plugins/azure/tasks/task_data.go
index abcd12cf..748d8eab 100644
--- a/plugins/azure/tasks/task_data.go
+++ b/plugins/azure/tasks/task_data.go
@@ -18,7 +18,7 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/azure/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/mitchellh/mapstructure"
@@ -51,7 +51,7 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*AzureOptions
 	}
 	// find the needed Azure now
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.BadInput.New("connectionId is invalid", errors.AsUserMessage())
 	}
 	return &op, nil
 }
diff --git a/plugins/bitbucket/api/blueprint.go b/plugins/bitbucket/api/blueprint.go
index 71951934..2039037f 100644
--- a/plugins/bitbucket/api/blueprint.go
+++ b/plugins/bitbucket/api/blueprint.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -118,10 +119,10 @@ func MakePipelinePlan(subtaskMetas []core.SubTaskMeta, connectionId uint64, scop
 			}
 			defer res.Body.Close()
 			if res.StatusCode != http.StatusOK {
-				return nil, fmt.Errorf(
+				return nil, errors.Default.New(fmt.Sprintf(
 					"unexpected status code when requesting repo detail %d %s",
 					res.StatusCode, res.Request.URL.String(),
-				)
+				))
 			}
 			body, err := ioutil.ReadAll(res.Body)
 			if err != nil {
diff --git a/plugins/bitbucket/api/connection.go b/plugins/bitbucket/api/connection.go
index 5969690c..90097673 100644
--- a/plugins/bitbucket/api/connection.go
+++ b/plugins/bitbucket/api/connection.go
@@ -20,6 +20,7 @@ package api
 import (
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"time"
 
@@ -44,12 +45,12 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	var connection models.TestConnectionRequest
 	err = mapstructure.Decode(input.Body, &connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	// validate
 	err = vld.Struct(connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not validate request parameters", errors.AsUserMessage())
 	}
 
 	// test connection
@@ -78,7 +79,7 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	}
 
 	if res.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+		return nil, errors.HttpStatus(res.StatusCode).New("unexpected status code when testing connection")
 	}
 	return nil, nil
 }
diff --git a/plugins/bitbucket/impl/impl.go b/plugins/bitbucket/impl/impl.go
index 1c702576..a079e935 100644
--- a/plugins/bitbucket/impl/impl.go
+++ b/plugins/bitbucket/impl/impl.go
@@ -18,7 +18,7 @@ limitations under the License.
 package impl
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/migration"
 	"github.com/apache/incubator-devlake/plugins/bitbucket/api"
 	"github.com/apache/incubator-devlake/plugins/bitbucket/models"
@@ -81,12 +81,12 @@ func (plugin Bitbucket) PrepareTaskData(taskCtx core.TaskContext, options map[st
 	connection := &models.BitbucketConnection{}
 	err = connectionHelper.FirstById(connection, op.ConnectionId)
 	if err != nil {
-		return nil, fmt.Errorf("unable to get bitbucket connection by the given connection ID: %v", err)
+		return nil, errors.Default.Wrap(err, "unable to get bitbucket connection by the given connection ID")
 	}
 
 	apiClient, err := tasks.CreateApiClient(taskCtx, connection)
 	if err != nil {
-		return nil, fmt.Errorf("unable to get bitbucket API client instance: %v", err)
+		return nil, errors.Default.Wrap(err, "unable to get bitbucket API client instance")
 	}
 
 	return &tasks.BitbucketTaskData{
diff --git a/plugins/bitbucket/tasks/api_client.go b/plugins/bitbucket/tasks/api_client.go
index 53c9b706..77ceccf1 100644
--- a/plugins/bitbucket/tasks/api_client.go
+++ b/plugins/bitbucket/tasks/api_client.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/bitbucket/models"
 	"net/http"
 	"strconv"
@@ -43,7 +44,7 @@ func CreateApiClient(taskCtx core.TaskContext, connection *models.BitbucketConne
 	})
 	apiClient.SetAfterFunction(func(res *http.Response) error {
 		if res.StatusCode == http.StatusUnauthorized {
-			return fmt.Errorf("authentication failed, please check your Basic Auth configuration")
+			return errors.Unauthorized.New("authentication failed, please check your Basic Auth configuration")
 		}
 		return nil
 	})
@@ -60,7 +61,7 @@ func CreateApiClient(taskCtx core.TaskContext, connection *models.BitbucketConne
 			}
 			rateLimit, err := strconv.Atoi(rateLimitHeader)
 			if err != nil {
-				return 0, 0, fmt.Errorf("failed to parse X-Request-Count header: %w", err)
+				return 0, 0, errors.Default.Wrap(err, "failed to parse X-Request-Count header")
 			}
 
 			return rateLimit, 1 * time.Minute, nil
diff --git a/plugins/bitbucket/tasks/api_common.go b/plugins/bitbucket/tasks/api_common.go
index a626239f..689426a4 100644
--- a/plugins/bitbucket/tasks/api_common.go
+++ b/plugins/bitbucket/tasks/api_common.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"github.com/apache/incubator-devlake/plugins/helper"
@@ -80,17 +81,17 @@ func GetRawMessageFromResponse(res *http.Response) ([]json.RawMessage, error) {
 		Values []json.RawMessage `json:"values"`
 	}
 	if res == nil {
-		return nil, fmt.Errorf("res is nil")
+		return nil, errors.Default.New("res is nil")
 	}
 	defer res.Body.Close()
 	resBody, err := ioutil.ReadAll(res.Body)
 	if err != nil {
-		return nil, fmt.Errorf("%w %s", err, res.Request.URL.String())
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("error reading response from %s", res.Request.URL.String()))
 	}
 
 	err = json.Unmarshal(resBody, &rawMessages)
 	if err != nil {
-		return nil, fmt.Errorf("%w %s %s", err, res.Request.URL.String(), string(resBody))
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("error decoding response from %s: raw response: %s", res.Request.URL.String(), string(resBody)))
 	}
 
 	return rawMessages.Values, nil
diff --git a/plugins/bitbucket/tasks/repo_extractor.go b/plugins/bitbucket/tasks/repo_extractor.go
index 29a29a6d..b21c9213 100644
--- a/plugins/bitbucket/tasks/repo_extractor.go
+++ b/plugins/bitbucket/tasks/repo_extractor.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/plugins/bitbucket/models"
@@ -92,7 +93,7 @@ func ExtractApiRepositories(taskCtx core.SubTaskContext) error {
 				return nil, err
 			}
 			if body.FullName == "" {
-				return nil, fmt.Errorf("repo %s/%s not found", data.Options.Owner, data.Options.Repo)
+				return nil, errors.NotFound.New(fmt.Sprintf("repo %s/%s not found", data.Options.Owner, data.Options.Repo))
 			}
 			results := make([]interface{}, 0, 1)
 			bitbucketRepository := &models.BitbucketRepo{
diff --git a/plugins/bitbucket/tasks/task_data.go b/plugins/bitbucket/tasks/task_data.go
index e28968b6..f1aae65b 100644
--- a/plugins/bitbucket/tasks/task_data.go
+++ b/plugins/bitbucket/tasks/task_data.go
@@ -18,7 +18,7 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/plugins/bitbucket/models"
@@ -49,10 +49,10 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*BitbucketOpt
 		return nil, err
 	}
 	if op.Owner == "" {
-		return nil, fmt.Errorf("owner is required for Bitbucket execution")
+		return nil, errors.Default.New("owner is required for Bitbucket execution")
 	}
 	if op.Repo == "" {
-		return nil, fmt.Errorf("repo is required for Bitbucket execution")
+		return nil, errors.Default.New("repo is required for Bitbucket execution")
 	}
 	if op.PrType == "" {
 		op.PrType = "type/(.*)$"
@@ -84,7 +84,7 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*BitbucketOpt
 
 	// find the needed Bitbucket now
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.Default.New("connectionId is invalid")
 	}
 	return &op, nil
 }
diff --git a/plugins/bitbucket/utils/utils.go b/plugins/bitbucket/utils/utils.go
index 14675b1c..93efab30 100644
--- a/plugins/bitbucket/utils/utils.go
+++ b/plugins/bitbucket/utils/utils.go
@@ -18,7 +18,7 @@ limitations under the License.
 package utils
 
 import (
-	"errors"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"net/http"
 	"regexp"
@@ -58,7 +58,7 @@ func ConvertRateLimitInfo(date string, resetTime string, remaining string) (Rate
 			return rateLimitInfo, err
 		}
 	} else {
-		return rateLimitInfo, errors.New("rate limit date was an empty string")
+		return rateLimitInfo, errors.Default.New("rate limit date was an empty string")
 	}
 	if resetTime != "" {
 		resetInt, err := strconv.ParseInt(resetTime, 10, 64)
@@ -67,7 +67,7 @@ func ConvertRateLimitInfo(date string, resetTime string, remaining string) (Rate
 		}
 		rateLimitInfo.ResetTime = time.Unix(resetInt, 0)
 	} else {
-		return rateLimitInfo, errors.New("rate limit reset time was an empty string")
+		return rateLimitInfo, errors.Default.New("rate limit reset time was an empty string")
 	}
 	if remaining != "" {
 		rateLimitInfo.Remaining, err = strconv.Atoi(remaining)
@@ -75,7 +75,7 @@ func ConvertRateLimitInfo(date string, resetTime string, remaining string) (Rate
 			return rateLimitInfo, err
 		}
 	} else {
-		return rateLimitInfo, errors.New("rate remaining was an empty string")
+		return rateLimitInfo, errors.Default.New("rate remaining was an empty string")
 	}
 	return rateLimitInfo, nil
 }
@@ -132,12 +132,12 @@ func GetPagingFromLinkHeader(link string) (PagingInfo, error) {
 				}
 
 			} else {
-				return result, errors.New("parsed string values aren't long enough")
+				return result, errors.Default.New("parsed string values aren't long enough")
 			}
 		}
 		return result, nil
 	} else {
-		return result, errors.New("the link string provided is invalid. There is likely no next page of data to fetch")
+		return result, errors.Default.New("the link string provided is invalid. There is likely no next page of data to fetch")
 	}
 }
 
@@ -147,6 +147,6 @@ func GetIssueIdByIssueUrl(s string) (int, error) {
 	if len(groups) > 0 {
 		return strconv.Atoi(groups[1])
 	} else {
-		return 0, errors.New("invalid issue url")
+		return 0, errors.Default.New("invalid issue url")
 	}
 }
diff --git a/plugins/core/hub.go b/plugins/core/hub.go
index 6ce4c826..973fdbb4 100644
--- a/plugins/core/hub.go
+++ b/plugins/core/hub.go
@@ -18,8 +18,8 @@ limitations under the License.
 package core
 
 import (
-	"errors"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"strings"
 )
 
@@ -37,12 +37,12 @@ func RegisterPlugin(name string, plugin PluginMeta) error {
 
 func GetPlugin(name string) (PluginMeta, error) {
 	if plugins == nil {
-		return nil, errors.New("RegisterPlugin have never been called.")
+		return nil, errors.Default.New("RegisterPlugin have never been called.")
 	}
 	if plugin, ok := plugins[name]; ok {
 		return plugin, nil
 	}
-	return nil, fmt.Errorf("Plugin `%s` doesn't exist", name)
+	return nil, errors.Default.New(fmt.Sprintf("Plugin `%s` doesn't exist", name))
 }
 
 type PluginCallBack func(name string, plugin PluginMeta) error
@@ -67,5 +67,5 @@ func FindPluginNameBySubPkgPath(subPkgPath string) (string, error) {
 			return name, nil
 		}
 	}
-	return "", fmt.Errorf("Unable to find plugin for subPkgPath %s", subPkgPath)
+	return "", errors.Default.New(fmt.Sprintf("Unable to find plugin for subPkgPath %s", subPkgPath))
 }
diff --git a/plugins/core/logger.go b/plugins/core/logger.go
index 0c71ab2f..79ebfd7e 100644
--- a/plugins/core/logger.go
+++ b/plugins/core/logger.go
@@ -25,10 +25,10 @@ import (
 type LogLevel logrus.Level
 
 const (
-	LOG_DEBUG LogLevel = LogLevel(logrus.DebugLevel)
-	LOG_INFO  LogLevel = LogLevel(logrus.InfoLevel)
-	LOG_WARN  LogLevel = LogLevel(logrus.WarnLevel)
-	LOG_ERROR LogLevel = LogLevel(logrus.ErrorLevel)
+	LOG_DEBUG = LogLevel(logrus.DebugLevel)
+	LOG_INFO  = LogLevel(logrus.InfoLevel)
+	LOG_WARN  = LogLevel(logrus.WarnLevel)
+	LOG_ERROR = LogLevel(logrus.ErrorLevel)
 )
 
 // Logger General logger interface, can be used anywhere
@@ -38,8 +38,10 @@ type Logger interface {
 	Log(level LogLevel, format string, a ...interface{})
 	Debug(format string, a ...interface{})
 	Info(format string, a ...interface{})
-	Warn(format string, a ...interface{})
-	Error(format string, a ...interface{})
+	// Warn logs a message. The err field can be left `nil`, otherwise the logger will format it into the message.
+	Warn(err error, format string, a ...interface{})
+	// Error logs an error. The err field can be left `nil`, otherwise the logger will format it into the message.
+	Error(err error, format string, a ...interface{})
 	// Nested return a new logger instance. `name` is the extra prefix to be prepended to each message. Leaving it blank
 	// will add no additional prefix. The new Logger will inherit the properties of the original.
 	Nested(name string) Logger
diff --git a/plugins/core/plugin_api.go b/plugins/core/plugin_api.go
index d7dfdd9b..a2047f53 100644
--- a/plugins/core/plugin_api.go
+++ b/plugins/core/plugin_api.go
@@ -47,15 +47,16 @@ type ApiResourceHandler func(input *ApiResourceInput) (*ApiResourceOutput, error
 
 // Implement this interface if plugin offered API
 // Code sample to register a api on `sources/:connectionId`:
-// func (plugin Jira) ApiResources() map[string]map[string]core.ApiResourceHandler {
-// 	return map[string]map[string]core.ApiResourceHandler{
-// 		"connections/:connectionId": {
-// 			"PUT":    api.PutConnection,
-// 			"DELETE": api.DeleteConnection,
-// 			"GET":    api.GetConnection,
-// 		},
-// 	}
-// }
+//
+//	func (plugin Jira) ApiResources() map[string]map[string]core.ApiResourceHandler {
+//		return map[string]map[string]core.ApiResourceHandler{
+//			"connections/:connectionId": {
+//				"PUT":    api.PutConnection,
+//				"DELETE": api.DeleteConnection,
+//				"GET":    api.GetConnection,
+//			},
+//		}
+//	}
 type PluginApi interface {
 	ApiResources() map[string]map[string]ApiResourceHandler
 }
diff --git a/plugins/core/plugin_utils.go b/plugins/core/plugin_utils.go
index 96fa6591..21e09eeb 100644
--- a/plugins/core/plugin_utils.go
+++ b/plugins/core/plugin_utils.go
@@ -24,6 +24,7 @@ import (
 	"crypto/sha256"
 	"encoding/base64"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"math/rand"
 	"time"
 )
@@ -48,12 +49,12 @@ func Encrypt(encKey, plainText string) (string, error) {
 	return base64.StdEncoding.EncodeToString(output), nil
 }
 
-//  Base64 + AES decryption using ENCODE_KEY in .env as key
+// Base64 + AES decryption using ENCODE_KEY in .env as key
 func Decrypt(encKey, encryptedText string) (string, error) {
 	// when encryption key is not set
 	if encKey == "" {
 		// return error message
-		return encryptedText, fmt.Errorf("encKey is required")
+		return encryptedText, errors.Default.New("encKey is required")
 	}
 
 	// Decode Base64
@@ -78,7 +79,7 @@ func Decrypt(encKey, encryptedText string) (string, error) {
 			return string(output), nil
 		}
 	}
-	return "", fmt.Errorf("invalid encKey")
+	return "", errors.Default.New("invalid encKey")
 }
 
 // PKCS7 padding
@@ -101,7 +102,7 @@ func PKCS7UnPadding(origData []byte) []byte {
 	return origData[:(length - unpadding)]
 }
 
-//AES encryption, CBC
+// AES encryption, CBC
 func AesEncrypt(origData, key []byte) ([]byte, error) {
 	// data alignment fill and encryption
 	sha256Key := sha256.Sum256(key)
@@ -119,7 +120,7 @@ func AesEncrypt(origData, key []byte) ([]byte, error) {
 	return crypted, nil
 }
 
-//AES decryption
+// AES decryption
 func AesDecrypt(crypted, key []byte) ([]byte, error) {
 	// Uniformly use sha256 to process as 32-bit Byte (256-bit bit)
 	sha256Key := sha256.Sum256(key)
@@ -131,7 +132,7 @@ func AesDecrypt(crypted, key []byte) ([]byte, error) {
 	// Get the block size and check whether the ciphertext length is legal
 	blockSize := block.BlockSize()
 	if len(crypted)%blockSize != 0 {
-		return nil, fmt.Errorf("The length of the data to be decrypted is [%d], so cannot match the required block size [%d]", len(crypted), blockSize)
+		return nil, errors.Default.New(fmt.Sprintf("The length of the data to be decrypted is [%d], so cannot match the required block size [%d]", len(crypted), blockSize))
 	}
 
 	// Decrypt and unalign data
diff --git a/plugins/dbt/dbt.go b/plugins/dbt/dbt.go
index 5fdbabd4..06d26381 100644
--- a/plugins/dbt/dbt.go
+++ b/plugins/dbt/dbt.go
@@ -18,7 +18,7 @@ limitations under the License.
 package main
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/dbt/tasks"
@@ -53,16 +53,16 @@ func (plugin Dbt) PrepareTaskData(taskCtx core.TaskContext, options map[string]i
 		return nil, err
 	}
 	if op.ProjectPath == "" {
-		return nil, fmt.Errorf("projectPath is required for dbt plugin")
+		return nil, errors.Default.New("projectPath is required for dbt plugin")
 	}
 	if op.ProjectName == "" {
-		return nil, fmt.Errorf("projectName is required for dbt plugin")
+		return nil, errors.Default.New("projectName is required for dbt plugin")
 	}
 	if op.ProjectTarget == "" {
 		op.ProjectTarget = "dev"
 	}
 	if op.SelectedModels == nil {
-		return nil, fmt.Errorf("selectedModels is required for dbt plugin")
+		return nil, errors.Default.New("selectedModels is required for dbt plugin")
 	}
 
 	return &tasks.DbtTaskData{
diff --git a/plugins/dbt/tasks/convertor.go b/plugins/dbt/tasks/convertor.go
index c65291ef..b86a8784 100644
--- a/plugins/dbt/tasks/convertor.go
+++ b/plugins/dbt/tasks/convertor.go
@@ -20,7 +20,7 @@ package tasks
 import (
 	"bufio"
 	"encoding/json"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net"
 	"net/url"
 	"os"
@@ -67,7 +67,7 @@ func DbtConverter(taskCtx core.SubTaskContext) error {
 		}
 		if value, ok := mapQuery["search_path"]; ok {
 			if len(value) < 1 {
-				return fmt.Errorf("DB_URL search_path parses error")
+				return errors.Default.New("DB_URL search_path parses error")
 			}
 			dbSchema = value[0]
 		} else {
@@ -115,7 +115,7 @@ func DbtConverter(taskCtx core.SubTaskContext) error {
 	if projectVars != nil {
 		jsonProjectVars, err := json.Marshal(projectVars)
 		if err != nil {
-			return fmt.Errorf("parameters vars json marshal error")
+			return errors.Default.New("parameters vars json marshal error")
 		}
 		dbtExecParams = append(dbtExecParams, "--vars")
 		dbtExecParams = append(dbtExecParams, string(jsonProjectVars))
@@ -143,7 +143,7 @@ func DbtConverter(taskCtx core.SubTaskContext) error {
 
 	cmd.Wait()
 	if !cmd.ProcessState.Success() {
-		log.Error("dbt run task error, please check!!!")
+		log.Error(nil, "dbt run task error, please check!!!")
 	}
 
 	return nil
diff --git a/plugins/dora/impl/impl.go b/plugins/dora/impl/impl.go
index e0fb04f8..5cb38042 100644
--- a/plugins/dora/impl/impl.go
+++ b/plugins/dora/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/migration"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/dora/api"
@@ -101,7 +102,7 @@ func (plugin Dora) ApiResources() map[string]map[string]core.ApiResourceHandler
 func (plugin Dora) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.DoraTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	// TODO
 	println(data)
diff --git a/plugins/feishu/api/connection.go b/plugins/feishu/api/connection.go
index 9b26d0a9..19027c91 100644
--- a/plugins/feishu/api/connection.go
+++ b/plugins/feishu/api/connection.go
@@ -19,7 +19,7 @@ package api
 
 import (
 	"context"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 
 	"github.com/apache/incubator-devlake/plugins/feishu/apimodels"
@@ -43,11 +43,11 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	var params models.TestConnectionRequest
 	err := mapstructure.Decode(input.Body, &params)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	err = vld.Struct(params)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not validate request parameters", errors.AsUserMessage())
 	}
 
 	authApiClient, err := helper.NewApiClient(context.TODO(), params.Endpoint, nil, 0, params.Proxy, basicRes)
@@ -70,7 +70,7 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 		return nil, err
 	}
 	if tokenResBody.AppAccessToken == "" && tokenResBody.TenantAccessToken == "" {
-		return nil, fmt.Errorf("failed to request access token")
+		return nil, errors.Default.New("failed to request access token")
 	}
 
 	// output
diff --git a/plugins/feishu/impl/impl.go b/plugins/feishu/impl/impl.go
index 0b748cd4..8f50be64 100644
--- a/plugins/feishu/impl/impl.go
+++ b/plugins/feishu/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/mitchellh/mapstructure"
 	"github.com/spf13/viper"
@@ -149,7 +150,7 @@ func (plugin Feishu) ApiResources() map[string]map[string]core.ApiResourceHandle
 func (plugin Feishu) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.FeishuTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/feishu/models/migrationscripts/20220714_add_init_tables.go b/plugins/feishu/models/migrationscripts/20220714_add_init_tables.go
index 585bea93..a463f6a6 100644
--- a/plugins/feishu/models/migrationscripts/20220714_add_init_tables.go
+++ b/plugins/feishu/models/migrationscripts/20220714_add_init_tables.go
@@ -19,7 +19,6 @@ package migrationscripts
 
 import (
 	"context"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 
diff --git a/plugins/feishu/tasks/api_client.go b/plugins/feishu/tasks/api_client.go
index b762a609..1c66dfe1 100644
--- a/plugins/feishu/tasks/api_client.go
+++ b/plugins/feishu/tasks/api_client.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/feishu/apimodels"
 	"github.com/apache/incubator-devlake/plugins/feishu/models"
 
@@ -51,7 +52,7 @@ func NewFeishuApiClient(taskCtx core.TaskContext, connection *models.FeishuConne
 		return nil, err
 	}
 	if tokenResBody.AppAccessToken == "" && tokenResBody.TenantAccessToken == "" {
-		return nil, fmt.Errorf("failed to request access token")
+		return nil, errors.Default.New("failed to request access token")
 	}
 	// real request apiClient
 	apiClient, err := helper.NewApiClient(taskCtx.GetContext(), ENDPOINT, nil, 0, connection.Proxy, taskCtx)
diff --git a/plugins/gitee/api/connection.go b/plugins/gitee/api/connection.go
index 3fd10948..06eee682 100644
--- a/plugins/gitee/api/connection.go
+++ b/plugins/gitee/api/connection.go
@@ -19,7 +19,7 @@ package api
 
 import (
 	"context"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 	"time"
@@ -44,11 +44,11 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	var connection models.TestConnectionRequest
 	err := mapstructure.Decode(input.Body, &connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	err = vld.Struct(connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not validate request parameters", errors.AsUserMessage())
 	}
 	// test connection
 	apiClient, err := helper.NewApiClient(
@@ -77,7 +77,7 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	}
 
 	if res.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+		return nil, errors.HttpStatus(res.StatusCode).New("unexpected status code when testing connection")
 	}
 	return nil, nil
 }
diff --git a/plugins/gitee/impl/impl.go b/plugins/gitee/impl/impl.go
index 57bb4bae..77850b33 100644
--- a/plugins/gitee/impl/impl.go
+++ b/plugins/gitee/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/apache/incubator-devlake/migration"
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -114,11 +115,11 @@ func (plugin Gitee) PrepareTaskData(taskCtx core.TaskContext, options map[string
 	}
 
 	if op.Owner == "" {
-		return nil, fmt.Errorf("owner is required for Gitee execution")
+		return nil, errors.BadInput.New("owner is required for Gitee execution", errors.AsUserMessage())
 	}
 
 	if op.Repo == "" {
-		return nil, fmt.Errorf("repo is required for Gitee execution")
+		return nil, errors.BadInput.New("repo is required for Gitee execution", errors.AsUserMessage())
 	}
 
 	if op.PrType == "" {
@@ -154,7 +155,7 @@ func (plugin Gitee) PrepareTaskData(taskCtx core.TaskContext, options map[string
 	}
 
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.BadInput.New("connectionId is invalid", errors.AsUserMessage())
 	}
 
 	connection := &models.GiteeConnection{}
@@ -212,7 +213,7 @@ func (plugin Gitee) ApiResources() map[string]map[string]core.ApiResourceHandler
 func (plugin Gitee) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.GiteeTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/gitee/tasks/api_client.go b/plugins/gitee/tasks/api_client.go
index 6594953e..abf766ad 100644
--- a/plugins/gitee/tasks/api_client.go
+++ b/plugins/gitee/tasks/api_client.go
@@ -18,7 +18,7 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"strconv"
 	"time"
@@ -52,7 +52,7 @@ func NewGiteeApiClient(taskCtx core.TaskContext, connection *models.GiteeConnect
 			}
 			rateLimit, err := strconv.Atoi(rateLimitHeader)
 			if err != nil {
-				return 0, 0, fmt.Errorf("failed to parse RateLimit-Limit header: %w", err)
+				return 0, 0, errors.Default.Wrap(err, "failed to parse RateLimit-Limit header")
 			}
 			// seems like gitlab rate limit is on minute basis
 			return rateLimit, 1 * time.Minute, nil
@@ -71,7 +71,7 @@ func NewGiteeApiClient(taskCtx core.TaskContext, connection *models.GiteeConnect
 
 func ignoreHTTPStatus404(res *http.Response) error {
 	if res.StatusCode == http.StatusUnauthorized {
-		return fmt.Errorf("authentication failed, please check your AccessToken")
+		return errors.Unauthorized.New("authentication failed, please check your AccessToken")
 	}
 	if res.StatusCode == http.StatusNotFound {
 		return helper.ErrIgnoreAndContinue
diff --git a/plugins/gitee/tasks/commit_collector.go b/plugins/gitee/tasks/commit_collector.go
index b6d86dee..75e493d3 100644
--- a/plugins/gitee/tasks/commit_collector.go
+++ b/plugins/gitee/tasks/commit_collector.go
@@ -18,7 +18,7 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/url"
 	"strconv"
 
@@ -56,7 +56,7 @@ func CollectApiCommits(taskCtx core.SubTaskContext) error {
 		)
 
 		if err != nil {
-			return fmt.Errorf("failed to get latest gitee commit record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest gitee commit record: %w")
 		}
 		if latestUpdated.Sha != "" {
 			since = &latestUpdated.CommittedDate
diff --git a/plugins/gitee/tasks/commit_extractor.go b/plugins/gitee/tasks/commit_extractor.go
index bd584255..1d82b3c3 100644
--- a/plugins/gitee/tasks/commit_extractor.go
+++ b/plugins/gitee/tasks/commit_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitee/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/gitee/tasks/commit_stats_collector.go b/plugins/gitee/tasks/commit_stats_collector.go
index 7afedd01..c6bcca9c 100644
--- a/plugins/gitee/tasks/commit_stats_collector.go
+++ b/plugins/gitee/tasks/commit_stats_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -58,7 +59,7 @@ func CollectApiCommitStats(taskCtx core.SubTaskContext) error {
 	)
 
 	if err != nil {
-		return fmt.Errorf("failed to get latest gitee commit record: %w", err)
+		return errors.Default.Wrap(err, "failed to get latest gitee commit record")
 	}
 
 	cursor, err := db.Cursor(
diff --git a/plugins/gitee/tasks/commit_stats_extractor.go b/plugins/gitee/tasks/commit_stats_extractor.go
index b9af9705..29a21733 100644
--- a/plugins/gitee/tasks/commit_stats_extractor.go
+++ b/plugins/gitee/tasks/commit_stats_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 
 	"github.com/apache/incubator-devlake/plugins/core"
diff --git a/plugins/gitee/tasks/issue_collector.go b/plugins/gitee/tasks/issue_collector.go
index 7b43e8d2..3c89317a 100644
--- a/plugins/gitee/tasks/issue_collector.go
+++ b/plugins/gitee/tasks/issue_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 
@@ -57,7 +58,7 @@ func CollectApiIssues(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest gitee issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest gitee issue record")
 		}
 		if latestUpdated.GiteeId > 0 {
 			since = &latestUpdated.GiteeUpdatedAt
diff --git a/plugins/gitee/tasks/issue_comment_collector.go b/plugins/gitee/tasks/issue_comment_collector.go
index acff7e49..5672503d 100644
--- a/plugins/gitee/tasks/issue_comment_collector.go
+++ b/plugins/gitee/tasks/issue_comment_collector.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/url"
 
 	"github.com/apache/incubator-devlake/plugins/core/dal"
@@ -58,7 +59,7 @@ func CollectApiIssueComments(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest gitee issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest gitee issue record")
 		}
 		var latestUpdatedPrComt models.GiteePullRequestComment
 		err = db.All(
@@ -69,7 +70,7 @@ func CollectApiIssueComments(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest gitee issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest gitee issue record")
 		}
 		if latestUpdatedIssueComment.GiteeId > 0 && latestUpdatedPrComt.GiteeId > 0 {
 			if latestUpdatedIssueComment.GiteeUpdatedAt.Before(latestUpdatedPrComt.GiteeUpdatedAt) {
diff --git a/plugins/gitee/tasks/issue_comment_extractor.go b/plugins/gitee/tasks/issue_comment_extractor.go
index 24c46699..37be4506 100644
--- a/plugins/gitee/tasks/issue_comment_extractor.go
+++ b/plugins/gitee/tasks/issue_comment_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 
 	"github.com/apache/incubator-devlake/plugins/core"
diff --git a/plugins/gitee/tasks/issue_extractor.go b/plugins/gitee/tasks/issue_extractor.go
index b81a3657..a3e0962c 100644
--- a/plugins/gitee/tasks/issue_extractor.go
+++ b/plugins/gitee/tasks/issue_extractor.go
@@ -19,14 +19,12 @@ package tasks
 
 import (
 	"encoding/json"
-	"fmt"
-	"regexp"
-	"runtime/debug"
-
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/models/domainlayer/ticket"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitee/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
+	"regexp"
 )
 
 var ExtractApiIssuesMeta = core.SubTaskMeta{
@@ -111,42 +109,42 @@ func ExtractApiIssues(taskCtx core.SubTaskContext) error {
 	if len(issueSeverity) > 0 {
 		issueSeverityRegex, err = regexp.Compile(issueSeverity)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueSeverity failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueSeverity failed")
 		}
 	}
 	var issueComponent = config.IssueComponent
 	if len(issueComponent) > 0 {
 		issueComponentRegex, err = regexp.Compile(issueComponent)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueComponent failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueComponent failed")
 		}
 	}
 	var issuePriority = config.IssuePriority
 	if len(issuePriority) > 0 {
 		issuePriorityRegex, err = regexp.Compile(issuePriority)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issuePriority failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issuePriority failed")
 		}
 	}
 	var issueTypeBug = config.IssueTypeBug
 	if len(issueTypeBug) > 0 {
 		issueTypeBugRegex, err = regexp.Compile(issueTypeBug)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueTypeBug failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueTypeBug failed")
 		}
 	}
 	var issueTypeRequirement = config.IssueTypeRequirement
 	if len(issueTypeRequirement) > 0 {
 		issueTypeRequirementRegex, err = regexp.Compile(issueTypeRequirement)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueTypeRequirement failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueTypeRequirement failed")
 		}
 	}
 	var issueTypeIncident = config.IssueTypeIncident
 	if len(issueTypeIncident) > 0 {
 		issueTypeIncidentRegex, err = regexp.Compile(issueTypeIncident)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueTypeIncident failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueTypeIncident failed")
 		}
 	}
 
diff --git a/plugins/gitee/tasks/pr_collector.go b/plugins/gitee/tasks/pr_collector.go
index f4bbe20a..ed9a54dd 100644
--- a/plugins/gitee/tasks/pr_collector.go
+++ b/plugins/gitee/tasks/pr_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 
@@ -58,7 +59,7 @@ func CollectApiPullRequests(taskCtx core.SubTaskContext) error {
 		)
 
 		if err != nil {
-			return fmt.Errorf("failed to get latest gitee issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest gitee issue record")
 		}
 		if latestUpdated.GiteeId > 0 {
 			since = &latestUpdated.GiteeUpdatedAt
diff --git a/plugins/gitee/tasks/pr_extractor.go b/plugins/gitee/tasks/pr_extractor.go
index 19c7377a..77f34192 100644
--- a/plugins/gitee/tasks/pr_extractor.go
+++ b/plugins/gitee/tasks/pr_extractor.go
@@ -19,13 +19,11 @@ package tasks
 
 import (
 	"encoding/json"
-	"fmt"
-	"regexp"
-	"runtime/debug"
-
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitee/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
+	"regexp"
 )
 
 var ExtractApiPullRequestsMeta = core.SubTaskMeta{
@@ -88,14 +86,14 @@ func ExtractApiPullRequests(taskCtx core.SubTaskContext) error {
 	if len(prType) > 0 {
 		labelTypeRegex, err = regexp.Compile(prType)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prType failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prType failed")
 		}
 	}
 	var prComponent = config.PrComponent
 	if len(prComponent) > 0 {
 		labelComponentRegex, err = regexp.Compile(prComponent)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prComponent failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prComponent failed")
 		}
 	}
 
diff --git a/plugins/gitee/tasks/pr_issue_enricher.go b/plugins/gitee/tasks/pr_issue_enricher.go
index e434e904..ec4123e6 100644
--- a/plugins/gitee/tasks/pr_issue_enricher.go
+++ b/plugins/gitee/tasks/pr_issue_enricher.go
@@ -18,10 +18,9 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 	"regexp"
-	"runtime/debug"
 	"strconv"
 	"strings"
 
@@ -53,7 +52,7 @@ func EnrichPullRequestIssues(taskCtx core.SubTaskContext) (err error) {
 	if len(prBodyClosePattern) > 0 {
 		prBodyCloseRegex, err = regexp.Compile(prBodyClosePattern)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prBodyClosePattern failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prBodyClosePattern failed")
 		}
 	}
 	charPattern := regexp.MustCompile(`[a-zA-Z\s,]+`)
diff --git a/plugins/gitee/tasks/repo_extractor.go b/plugins/gitee/tasks/repo_extractor.go
index d465c024..61359b50 100644
--- a/plugins/gitee/tasks/repo_extractor.go
+++ b/plugins/gitee/tasks/repo_extractor.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitee/models"
@@ -57,7 +58,7 @@ func ExtractApiRepositories(taskCtx core.SubTaskContext) error {
 				return nil, err
 			}
 			if repo.GiteeId == 0 {
-				return nil, fmt.Errorf("repo %s/%s not found", data.Options.Owner, data.Options.Repo)
+				return nil, errors.NotFound.New(fmt.Sprintf("repo %s/%s not found", data.Options.Owner, data.Options.Repo))
 			}
 			results := make([]interface{}, 0, 1)
 			giteeRepository := &models.GiteeRepo{
diff --git a/plugins/gitee/tasks/shared.go b/plugins/gitee/tasks/shared.go
index e6ae9c1a..59f83485 100644
--- a/plugins/gitee/tasks/shared.go
+++ b/plugins/gitee/tasks/shared.go
@@ -19,8 +19,8 @@ package tasks
 
 import (
 	"encoding/json"
-	"errors"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/ioutil"
 	"net/http"
 	"regexp"
@@ -73,17 +73,17 @@ func GetRawMessageFromResponse(res *http.Response) ([]json.RawMessage, error) {
 	var rawMessages []json.RawMessage
 
 	if res == nil {
-		return nil, fmt.Errorf("res is nil")
+		return nil, errors.Default.New("res is nil")
 	}
 	defer res.Body.Close()
 	resBody, err := ioutil.ReadAll(res.Body)
 	if err != nil {
-		return nil, fmt.Errorf("%w %s", err, res.Request.URL.String())
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("error reading response from %s", res.Request.URL.String()))
 	}
 
 	err = json.Unmarshal(resBody, &rawMessages)
 	if err != nil {
-		return nil, fmt.Errorf("%w %s %s", err, res.Request.URL.String(), string(resBody))
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("error decoding response from %s: raw response: %s", res.Request.URL.String(), string(resBody)))
 	}
 
 	return rawMessages, nil
@@ -112,7 +112,7 @@ func ConvertRateLimitInfo(date string, resetTime string, remaining string) (Rate
 			return rateLimitInfo, err
 		}
 	} else {
-		return rateLimitInfo, errors.New("rate limit date was an empty string")
+		return rateLimitInfo, errors.Default.New("rate limit date was an empty string")
 	}
 	if resetTime != "" {
 		resetInt, err := strconv.ParseInt(resetTime, 10, 64)
@@ -121,7 +121,7 @@ func ConvertRateLimitInfo(date string, resetTime string, remaining string) (Rate
 		}
 		rateLimitInfo.ResetTime = time.Unix(resetInt, 0)
 	} else {
-		return rateLimitInfo, errors.New("rate limit reset time was an empty string")
+		return rateLimitInfo, errors.Default.New("rate limit reset time was an empty string")
 	}
 	if remaining != "" {
 		rateLimitInfo.Remaining, err = strconv.Atoi(remaining)
@@ -129,7 +129,7 @@ func ConvertRateLimitInfo(date string, resetTime string, remaining string) (Rate
 			return rateLimitInfo, err
 		}
 	} else {
-		return rateLimitInfo, errors.New("rate remaining was an empty string")
+		return rateLimitInfo, errors.Default.New("rate remaining was an empty string")
 	}
 	return rateLimitInfo, nil
 }
@@ -185,11 +185,11 @@ func GetPagingFromLinkHeader(link string) (PagingInfo, error) {
 					result.Prev = pageNumberInt
 				}
 			} else {
-				return result, errors.New("parsed string values aren't long enough")
+				return result, errors.Default.New("parsed string values aren't long enough")
 			}
 		}
 		return result, nil
 	} else {
-		return result, errors.New("the link string provided is invalid. There is likely no next page of data to fetch")
+		return result, errors.Default.New("the link string provided is invalid. There is likely no next page of data to fetch")
 	}
 }
diff --git a/plugins/gitextractor/main.go b/plugins/gitextractor/main.go
index 330b004b..faaeaa2f 100644
--- a/plugins/gitextractor/main.go
+++ b/plugins/gitextractor/main.go
@@ -20,7 +20,6 @@ package main
 import (
 	"context"
 	"flag"
-
 	"github.com/apache/incubator-devlake/config"
 	"github.com/apache/incubator-devlake/logger"
 	"github.com/apache/incubator-devlake/plugins/gitextractor/models"
diff --git a/plugins/gitextractor/parser/repo.go b/plugins/gitextractor/parser/repo.go
index d298d947..a8c95791 100644
--- a/plugins/gitextractor/parser/repo.go
+++ b/plugins/gitextractor/parser/repo.go
@@ -320,7 +320,7 @@ func (r *GitRepo) storeCommitFilesFromDiff(commitSha string, diff *git.Diff, com
 		if commitFile != nil {
 			err = r.store.CommitFiles(commitFile)
 			if err != nil {
-				r.logger.Error("CommitFiles error:", err)
+				r.logger.Error(err, "CommitFiles error")
 				return nil, err
 			}
 		}
@@ -357,13 +357,13 @@ func (r *GitRepo) storeCommitFilesFromDiff(commitSha string, diff *git.Diff, com
 	if commitFileComponent != nil {
 		err = r.store.CommitFileComponents(commitFileComponent)
 		if err != nil {
-			r.logger.Error("CommitFileComponents error:", err)
+			r.logger.Error(err, "CommitFileComponents error")
 		}
 	}
 	if commitFile != nil {
 		err = r.store.CommitFiles(commitFile)
 		if err != nil {
-			r.logger.Error("CommitFiles error:", err)
+			r.logger.Error(err, "CommitFiles error")
 		}
 	}
 	return err
diff --git a/plugins/gitextractor/tasks/git_repo_collector.go b/plugins/gitextractor/tasks/git_repo_collector.go
index 12816cba..9fda5a04 100644
--- a/plugins/gitextractor/tasks/git_repo_collector.go
+++ b/plugins/gitextractor/tasks/git_repo_collector.go
@@ -18,7 +18,7 @@ limitations under the License.
 package tasks
 
 import (
-	"errors"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/gitextractor/parser"
 	"strings"
 
@@ -37,17 +37,17 @@ type GitExtractorOptions struct {
 
 func (o GitExtractorOptions) Valid() error {
 	if o.RepoId == "" {
-		return errors.New("empty repoId")
+		return errors.BadInput.New("empty repoId", errors.AsUserMessage())
 	}
 	if o.Url == "" {
-		return errors.New("empty url")
+		return errors.BadInput.New("empty url", errors.AsUserMessage())
 	}
 	url := strings.TrimPrefix(o.Url, "ssh://")
 	if !(strings.HasPrefix(o.Url, "http") || strings.HasPrefix(url, "git@") || strings.HasPrefix(o.Url, "/")) {
-		return errors.New("wrong url")
+		return errors.BadInput.New("wrong url", errors.AsUserMessage())
 	}
 	if o.Proxy != "" && !strings.HasPrefix(o.Proxy, "http://") {
-		return errors.New("only support http proxy")
+		return errors.BadInput.New("only support http proxy", errors.AsUserMessage())
 	}
 	return nil
 }
@@ -55,7 +55,7 @@ func (o GitExtractorOptions) Valid() error {
 func CollectGitCommits(subTaskCtx core.SubTaskContext) error {
 	repo := getGitRepo(subTaskCtx)
 	if count, err := repo.CountCommits(subTaskCtx.GetContext()); err != nil {
-		subTaskCtx.GetLogger().Error("unable to get commit count: %v", err)
+		subTaskCtx.GetLogger().Error(err, "unable to get commit count")
 		subTaskCtx.SetProgress(0, -1)
 	} else {
 		subTaskCtx.SetProgress(0, count)
@@ -66,7 +66,7 @@ func CollectGitCommits(subTaskCtx core.SubTaskContext) error {
 func CollectGitBranches(subTaskCtx core.SubTaskContext) error {
 	repo := getGitRepo(subTaskCtx)
 	if count, err := repo.CountBranches(subTaskCtx.GetContext()); err != nil {
-		subTaskCtx.GetLogger().Error("unable to get branch count: %v", err)
+		subTaskCtx.GetLogger().Error(err, "unable to get branch count")
 		subTaskCtx.SetProgress(0, -1)
 	} else {
 		subTaskCtx.SetProgress(0, count)
@@ -77,7 +77,7 @@ func CollectGitBranches(subTaskCtx core.SubTaskContext) error {
 func CollectGitTags(subTaskCtx core.SubTaskContext) error {
 	repo := getGitRepo(subTaskCtx)
 	if count, err := repo.CountTags(); err != nil {
-		subTaskCtx.GetLogger().Error("unable to get tag count: %v", err)
+		subTaskCtx.GetLogger().Error(err, "unable to get tag count")
 		subTaskCtx.SetProgress(0, -1)
 	} else {
 		subTaskCtx.SetProgress(0, count)
diff --git a/plugins/github/api/blueprint.go b/plugins/github/api/blueprint.go
index a181f527..a3eef25f 100644
--- a/plugins/github/api/blueprint.go
+++ b/plugins/github/api/blueprint.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -118,10 +119,8 @@ func MakePipelinePlan(subtaskMetas []core.SubTaskMeta, connectionId uint64, scop
 			}
 			defer res.Body.Close()
 			if res.StatusCode != http.StatusOK {
-				return nil, fmt.Errorf(
-					"unexpected status code when requesting repo detail %d %s",
-					res.StatusCode, res.Request.URL.String(),
-				)
+				return nil, errors.HttpStatus(res.StatusCode).New(fmt.Sprintf(
+					"unexpected status code when requesting repo detail from %s", res.Request.URL.String()))
 			}
 			body, err := ioutil.ReadAll(res.Body)
 			if err != nil {
diff --git a/plugins/github/api/connection.go b/plugins/github/api/connection.go
index 3cff808c..41b1c4cf 100644
--- a/plugins/github/api/connection.go
+++ b/plugins/github/api/connection.go
@@ -20,6 +20,7 @@ package api
 import (
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"strings"
 	"time"
@@ -50,11 +51,11 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	var params models.TestConnectionRequest
 	err := mapstructure.Decode(input.Body, &params)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	err = vld.Struct(params)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not validate request parameters", errors.AsUserMessage())
 	}
 	tokens := strings.Split(params.Token, ",")
 
@@ -79,21 +80,21 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 				basicRes,
 			)
 			if err != nil {
-				results <- VerifyResult{err: fmt.Errorf("verify token failed for #%v %s %w", j, token, err)}
+				results <- VerifyResult{err: errors.Default.Wrap(err, fmt.Sprintf("verify token failed for #%d %s", j, token), errors.AsUserMessage())}
 				return
 			}
 			res, err := apiClient.Get("user", nil, nil)
 			if err != nil {
-				results <- VerifyResult{err: fmt.Errorf("verify token failed for #%v %s %w", j, token, err)}
+				results <- VerifyResult{err: errors.Default.Wrap(err, fmt.Sprintf("verify token failed for #%d %s", j, token), errors.AsUserMessage())}
 				return
 			}
 			githubUserOfToken := &models.GithubUserOfToken{}
 			err = helper.UnmarshalResponse(res, githubUserOfToken)
 			if err != nil {
-				results <- VerifyResult{err: fmt.Errorf("verify token failed for #%v %s %w", j, token, err)}
+				results <- VerifyResult{err: errors.Default.Wrap(err, fmt.Sprintf("verify token failed for #%v %s", j, token), errors.AsUserMessage())}
 				return
 			} else if githubUserOfToken.Login == "" {
-				results <- VerifyResult{err: fmt.Errorf("invalid token for #%v %s", j, token)}
+				results <- VerifyResult{err: errors.Default.Wrap(err, fmt.Sprintf("invalid token for #%v %s", j, token), errors.AsUserMessage())}
 				return
 			}
 			results <- VerifyResult{login: githubUserOfToken.Login}
@@ -115,7 +116,7 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 		}
 	}
 	if len(msgs) > 0 {
-		return nil, fmt.Errorf(strings.Join(msgs, "\n"))
+		return nil, errors.Default.New(strings.Join(msgs, "\n"))
 	}
 
 	githubApiResponse := GithubTestConnResponse{}
diff --git a/plugins/github/e2e/raw_tables/_raw_github_api_pull_request_review_comments.csv b/plugins/github/e2e/raw_tables/_raw_github_api_pull_request_review_comments.csv
index 5d4fc661..63bd3218 100644
--- a/plugins/github/e2e/raw_tables/_raw_github_api_pull_request_review_comments.csv
+++ b/plugins/github/e2e/raw_tables/_raw_github_api_pull_request_review_comments.csv
@@ -12,6 +12,6 @@ id,params,data,url,input,created_at
 11,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/pulls/comments/315493235"",""pull_request_review_id"":276914383,""id"":315493235,""node_id"":""MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDMxNTQ5MzIzNQ=="",""diff_hunk"":""@@ -416,6 +416,78 @@ func TestPurgePreMalloc(t *testing.T) {\n \t}\n }\n \n+func TestNonblockingSubmit(t *testing.T) {\n+\tpoolSize := 10\n+\tp, err := ants.NewPool(poolSize)\n+\tif err != nil {\n+\t\tt [...]
 12,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/pulls/comments/315497949"",""pull_request_review_id"":276920486,""id"":315497949,""node_id"":""MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDMxNTQ5Nzk0OQ=="",""diff_hunk"":""@@ -236,8 +253,18 @@ func (p *Pool) retrieveWorker() *Worker {\n \t\tp.lock.Unlock()\n \t\tspawnWorker()\n \t} else {\n+\t\tif p.NonblockingSubmit {\n+\t\t\tp.lock.Unlock()\n+\t\t\treturn nil\n+\t\t}\n  [...]
 13,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/pulls/comments/315498393"",""pull_request_review_id"":276920486,""id"":315498393,""node_id"":""MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDMxNTQ5ODM5Mw=="",""diff_hunk"":""@@ -151,7 +164,11 @@ func (p *Pool) Submit(task func()) error {\n \tif atomic.LoadInt32(&p.release) == CLOSED {\n \t\treturn ErrPoolClosed\n \t}\n-\tp.retrieveWorker().task <- task\n+\tw := p.retrieveWo [...]
-14,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/pulls/comments/315498823"",""pull_request_review_id"":276920486,""id"":315498823,""node_id"":""MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDMxNTQ5ODgyMw=="",""diff_hunk"":""@@ -51,6 +51,9 @@ var (\n \n \t// ErrPoolClosed will be returned when submitting task to a closed pool.\n \tErrPoolClosed = errors.New(\""this pool has been closed\"")\n+\n+\t// ErrPoolBlocked will be r [...]
+14,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/pulls/comments/315498823"",""pull_request_review_id"":276920486,""id"":315498823,""node_id"":""MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDMxNTQ5ODgyMw=="",""diff_hunk"":""@@ -51,6 +51,9 @@ var (\n \n \t// ErrPoolClosed will be returned when submitting task to a closed pool.\n \tErrPoolClosed = errors.Default.New(\""this pool has been closed\"")\n+\n+\t// ErrPoolBlocked w [...]
 15,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/pulls/comments/315499067"",""pull_request_review_id"":276920486,""id"":315499067,""node_id"":""MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDMxNTQ5OTA2Nw=="",""diff_hunk"":""@@ -60,6 +60,19 @@ type Pool struct {\n \t// PanicHandler is used to handle panics from each worker goroutine.\n \t// if nil, panics will be thrown out again from worker goroutines.\n \tPanicHandler fun [...]
 16,"{""ConnectionId"":1,""Owner"":""panjf2000"",""Repo"":""ants""}","{""url"":""https://api.github.com/repos/panjf2000/ants/pulls/comments/315499281"",""pull_request_review_id"":276920486,""id"":315499281,""node_id"":""MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDMxNTQ5OTI4MQ=="",""diff_hunk"":""@@ -60,6 +60,19 @@ type Pool struct {\n \t// PanicHandler is used to handle panics from each worker goroutine.\n \t// if nil, panics will be thrown out again from worker goroutines.\n \tPanicHandler fun [...]
\ No newline at end of file
diff --git a/plugins/github/impl/impl.go b/plugins/github/impl/impl.go
index 80116162..3e461861 100644
--- a/plugins/github/impl/impl.go
+++ b/plugins/github/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/migration"
@@ -145,20 +146,20 @@ func (plugin Github) PrepareTaskData(taskCtx core.TaskContext, options map[strin
 	connection := &models.GithubConnection{}
 	err = connectionHelper.FirstById(connection, op.ConnectionId)
 	if err != nil {
-		return nil, fmt.Errorf("unable to get github connection by the given connection ID: %v", err)
+		return nil, errors.Default.Wrap(err, "unable to get github connection by the given connection ID", errors.AsUserMessage())
 	}
 
 	var since time.Time
 	if op.Since != "" {
 		since, err = time.Parse("2006-01-02T15:04:05Z", op.Since)
 		if err != nil {
-			return nil, fmt.Errorf("invalid value for `since`: %w", err)
+			return nil, errors.BadInput.Wrap(err, "invalid value for `since`", errors.AsUserMessage())
 		}
 	}
 
 	apiClient, err := tasks.CreateApiClient(taskCtx, connection)
 	if err != nil {
-		return nil, fmt.Errorf("unable to get github API client instance: %v", err)
+		return nil, errors.Default.Wrap(err, "unable to get github API client instance", errors.AsUserMessage())
 	}
 	taskData := &tasks.GithubTaskData{
 		Options:   op,
@@ -204,7 +205,7 @@ func (plugin Github) MakePipelinePlan(connectionId uint64, scope []*core.Bluepri
 func (plugin Github) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.GithubTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/github/models/migrationscripts/20220715_add_init_tables.go b/plugins/github/models/migrationscripts/20220715_add_init_tables.go
index 3a431852..07e33c61 100644
--- a/plugins/github/models/migrationscripts/20220715_add_init_tables.go
+++ b/plugins/github/models/migrationscripts/20220715_add_init_tables.go
@@ -19,7 +19,6 @@ package migrationscripts
 
 import (
 	"context"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 
diff --git a/plugins/github/models/migrationscripts/20220728_add_runs_table.go b/plugins/github/models/migrationscripts/20220728_add_runs_table.go
index aee05c57..10953098 100644
--- a/plugins/github/models/migrationscripts/20220728_add_runs_table.go
+++ b/plugins/github/models/migrationscripts/20220728_add_runs_table.go
@@ -19,7 +19,7 @@ package migrationscripts
 
 import (
 	"context"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/models/migrationscripts/archived"
@@ -69,7 +69,7 @@ func (u *addGithubRunsTable) Up(ctx context.Context, db *gorm.DB) error {
 	// create table
 	err := db.Migrator().CreateTable(GithubRun20220728{})
 	if err != nil {
-		return fmt.Errorf("create table _tool_github_runs error")
+		return errors.Default.Wrap(err, "create table _tool_github_runs error")
 	}
 	return nil
 
diff --git a/plugins/github/models/migrationscripts/20220729_add_jobs_table.go b/plugins/github/models/migrationscripts/20220729_add_jobs_table.go
index 9631dceb..4151a98d 100644
--- a/plugins/github/models/migrationscripts/20220729_add_jobs_table.go
+++ b/plugins/github/models/migrationscripts/20220729_add_jobs_table.go
@@ -19,7 +19,7 @@ package migrationscripts
 
 import (
 	"context"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/models/migrationscripts/archived"
@@ -62,7 +62,7 @@ func (u *addGithubJobsTable) Up(ctx context.Context, db *gorm.DB) error {
 	// create table
 	err := db.Migrator().CreateTable(GithubJob20220729{})
 	if err != nil {
-		return fmt.Errorf("create table _tool_github_jobs error")
+		return errors.Default.Wrap(err, "create table _tool_github_jobs error")
 	}
 	return nil
 
diff --git a/plugins/github/models/migrationscripts/20220802_add_pipeline_table.go b/plugins/github/models/migrationscripts/20220802_add_pipeline_table.go
index 84e1c87a..141071de 100644
--- a/plugins/github/models/migrationscripts/20220802_add_pipeline_table.go
+++ b/plugins/github/models/migrationscripts/20220802_add_pipeline_table.go
@@ -19,7 +19,7 @@ package migrationscripts
 
 import (
 	"context"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/models/migrationscripts/archived"
@@ -50,7 +50,7 @@ func (u *addGithubPipelineTable) Up(ctx context.Context, db *gorm.DB) error {
 	// create table
 	err := db.Migrator().CreateTable(GithubPipeline20220803{})
 	if err != nil {
-		return fmt.Errorf("create table _tool_github_pipelines error")
+		return errors.Default.Wrap(err, "create table _tool_github_pipelines error")
 	}
 	return nil
 
diff --git a/plugins/github/tasks/api_client.go b/plugins/github/tasks/api_client.go
index 131467c3..f6572be5 100644
--- a/plugins/github/tasks/api_client.go
+++ b/plugins/github/tasks/api_client.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"strconv"
 	"strings"
@@ -55,21 +56,21 @@ func CreateApiClient(taskCtx core.TaskContext, connection *models.GithubConnecti
 			/* calculate by number of remaining requests
 			remaining, err := strconv.Atoi(res.Header.Get("X-RateLimit-Remaining"))
 			if err != nil {
-				return 0,0, fmt.Errorf("failed to parse X-RateLimit-Remaining header: %w", err)
+				return 0,0, errors.Default.New("failed to parse X-RateLimit-Remaining header: %w", err)
 			}
 			reset, err := strconv.Atoi(res.Header.Get("X-RateLimit-Reset"))
 			if err != nil {
-				return 0, 0, fmt.Errorf("failed to parse X-RateLimit-Reset header: %w", err)
+				return 0, 0, errors.Default.New("failed to parse X-RateLimit-Reset header: %w", err)
 			}
 			date, err := http.ParseTime(res.Header.Get("Date"))
 			if err != nil {
-				return 0, 0, fmt.Errorf("failed to parse Date header: %w", err)
+				return 0, 0, errors.Default.New("failed to parse Date header: %w", err)
 			}
 			return remaining * len(tokens), time.Unix(int64(reset), 0).Sub(date), nil
 			*/
 			rateLimit, err := strconv.Atoi(res.Header.Get("X-RateLimit-Limit"))
 			if err != nil {
-				return 0, 0, fmt.Errorf("failed to parse X-RateLimit-Limit header: %w", err)
+				return 0, 0, errors.Default.Wrap(err, "failed to parse X-RateLimit-Limit header")
 			}
 			// even though different token could have different rate limit, but it is hard to support it
 			// so, we calculate the rate limit of a single token, and presume all tokens are the same, to
diff --git a/plugins/github/tasks/cicd_job_extractor.go b/plugins/github/tasks/cicd_job_extractor.go
index b8e8fcae..3395c0c0 100644
--- a/plugins/github/tasks/cicd_job_extractor.go
+++ b/plugins/github/tasks/cicd_job_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/github/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/github/tasks/cicd_run_enricher.go b/plugins/github/tasks/cicd_run_enricher.go
index a266adb5..516dcf9c 100644
--- a/plugins/github/tasks/cicd_run_enricher.go
+++ b/plugins/github/tasks/cicd_run_enricher.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	githubModels "github.com/apache/incubator-devlake/plugins/github/models"
diff --git a/plugins/github/tasks/cicd_run_extractor.go b/plugins/github/tasks/cicd_run_extractor.go
index 703bc652..d188e4e1 100644
--- a/plugins/github/tasks/cicd_run_extractor.go
+++ b/plugins/github/tasks/cicd_run_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/github/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/github/tasks/comment_collector.go b/plugins/github/tasks/comment_collector.go
index d7192260..aeb1bd7c 100644
--- a/plugins/github/tasks/comment_collector.go
+++ b/plugins/github/tasks/comment_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 
@@ -51,7 +52,7 @@ func CollectApiComments(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest github issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest github issue record")
 		}
 		var latestUpdatedPrComt models.GithubPrComment
 		err = db.All(
@@ -62,7 +63,7 @@ func CollectApiComments(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest github issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest github issue record")
 		}
 		if latestUpdatedIssueComt.GithubId > 0 && latestUpdatedPrComt.GithubId > 0 {
 			if latestUpdatedIssueComt.GithubUpdatedAt.Before(latestUpdatedPrComt.GithubUpdatedAt) {
diff --git a/plugins/github/tasks/commit_collector.go b/plugins/github/tasks/commit_collector.go
index 7bd4e99c..9331058d 100644
--- a/plugins/github/tasks/commit_collector.go
+++ b/plugins/github/tasks/commit_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 
@@ -59,7 +60,7 @@ func CollectApiCommits(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest github commit record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest github commit record")
 		}
 		if latestUpdated.Sha != "" {
 			since = &latestUpdated.CommittedDate
diff --git a/plugins/github/tasks/commit_extractor.go b/plugins/github/tasks/commit_extractor.go
index 418bce5c..f08c5b7e 100644
--- a/plugins/github/tasks/commit_extractor.go
+++ b/plugins/github/tasks/commit_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/github/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/github/tasks/commit_stats_collector.go b/plugins/github/tasks/commit_stats_collector.go
index 50716fbe..b9541762 100644
--- a/plugins/github/tasks/commit_stats_collector.go
+++ b/plugins/github/tasks/commit_stats_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"io/ioutil"
 	"net/http"
@@ -55,7 +56,7 @@ func CollectApiCommitStats(taskCtx core.SubTaskContext) error {
 		dal.Limit(1),
 	)
 	if err != nil && err != gorm.ErrRecordNotFound {
-		return fmt.Errorf("failed to get latest github commit record: %w", err)
+		return errors.Default.Wrap(err, "failed to get latest github commit record")
 	}
 
 	cursor, err := db.Cursor(
diff --git a/plugins/github/tasks/commit_stats_extractor.go b/plugins/github/tasks/commit_stats_extractor.go
index 87b88892..f1161029 100644
--- a/plugins/github/tasks/commit_stats_extractor.go
+++ b/plugins/github/tasks/commit_stats_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"gorm.io/gorm"
 
diff --git a/plugins/github/tasks/event_collector.go b/plugins/github/tasks/event_collector.go
index 5ef2d624..16c488cc 100644
--- a/plugins/github/tasks/event_collector.go
+++ b/plugins/github/tasks/event_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 
@@ -61,7 +62,7 @@ func CollectApiEvents(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest github issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest github issue record")
 		}
 
 		if latestUpdatedIssueEvent.GithubId > 0 {
diff --git a/plugins/github/tasks/event_extractor.go b/plugins/github/tasks/event_extractor.go
index 8d87c2db..390d972b 100644
--- a/plugins/github/tasks/event_extractor.go
+++ b/plugins/github/tasks/event_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/github/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/github/tasks/issue_collector.go b/plugins/github/tasks/issue_collector.go
index 508ff437..fdf64b9d 100644
--- a/plugins/github/tasks/issue_collector.go
+++ b/plugins/github/tasks/issue_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 
@@ -64,7 +65,7 @@ func CollectApiIssues(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest github issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest github issue record")
 		}
 		if latestUpdated.GithubId > 0 {
 			since = &latestUpdated.GithubUpdatedAt
diff --git a/plugins/github/tasks/issue_extractor.go b/plugins/github/tasks/issue_extractor.go
index 9dd8ea70..d58d71eb 100644
--- a/plugins/github/tasks/issue_extractor.go
+++ b/plugins/github/tasks/issue_extractor.go
@@ -19,14 +19,12 @@ package tasks
 
 import (
 	"encoding/json"
-	"fmt"
-	"regexp"
-	"runtime/debug"
-
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/models/domainlayer/ticket"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/github/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
+	"regexp"
 )
 
 var ExtractApiIssuesMeta = core.SubTaskMeta{
@@ -232,42 +230,42 @@ func NewIssueRegexes(config models.TransformationRules) (*IssueRegexes, error) {
 	if len(issueSeverity) > 0 {
 		issueRegexes.SeverityRegex, err = regexp.Compile(issueSeverity)
 		if err != nil {
-			return nil, fmt.Errorf("regexp Compile issueSeverity failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return nil, errors.Default.Wrap(err, "regexp Compile issueSeverity failed")
 		}
 	}
 	var issueComponent = config.IssueComponent
 	if len(issueComponent) > 0 {
 		issueRegexes.ComponentRegex, err = regexp.Compile(issueComponent)
 		if err != nil {
-			return nil, fmt.Errorf("regexp Compile issueComponent failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return nil, errors.Default.Wrap(err, "regexp Compile issueComponent failed")
 		}
 	}
 	var issuePriority = config.IssuePriority
 	if len(issuePriority) > 0 {
 		issueRegexes.PriorityRegex, err = regexp.Compile(issuePriority)
 		if err != nil {
-			return nil, fmt.Errorf("regexp Compile issuePriority failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return nil, errors.Default.Wrap(err, "regexp Compile issuePriority failed")
 		}
 	}
 	var issueTypeBug = config.IssueTypeBug
 	if len(issueTypeBug) > 0 {
 		issueRegexes.TypeBugRegex, err = regexp.Compile(issueTypeBug)
 		if err != nil {
-			return nil, fmt.Errorf("regexp Compile issueTypeBug failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return nil, errors.Default.Wrap(err, "regexp Compile issueTypeBug failed")
 		}
 	}
 	var issueTypeRequirement = config.IssueTypeRequirement
 	if len(issueTypeRequirement) > 0 {
 		issueRegexes.TypeRequirementRegex, err = regexp.Compile(issueTypeRequirement)
 		if err != nil {
-			return nil, fmt.Errorf("regexp Compile issueTypeRequirement failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return nil, errors.Default.Wrap(err, "regexp Compile issueTypeRequirement failed")
 		}
 	}
 	var issueTypeIncident = config.IssueTypeIncident
 	if len(issueTypeIncident) > 0 {
 		issueRegexes.TypeIncidentRegex, err = regexp.Compile(issueTypeIncident)
 		if err != nil {
-			return nil, fmt.Errorf("regexp Compile issueTypeIncident failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return nil, errors.Default.Wrap(err, "regexp Compile issueTypeIncident failed")
 		}
 	}
 	return &issueRegexes, nil
diff --git a/plugins/github/tasks/pr_collector.go b/plugins/github/tasks/pr_collector.go
index 23263209..0b22d6b9 100644
--- a/plugins/github/tasks/pr_collector.go
+++ b/plugins/github/tasks/pr_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 
@@ -60,7 +61,7 @@ func CollectApiPullRequests(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest github issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest github issue record")
 		}
 		if latestUpdated.GithubId > 0 {
 			since = &latestUpdated.GithubUpdatedAt
diff --git a/plugins/github/tasks/pr_extractor.go b/plugins/github/tasks/pr_extractor.go
index a05289b8..dfb88cd6 100644
--- a/plugins/github/tasks/pr_extractor.go
+++ b/plugins/github/tasks/pr_extractor.go
@@ -19,13 +19,11 @@ package tasks
 
 import (
 	"encoding/json"
-	"fmt"
-	"regexp"
-	"runtime/debug"
-
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/github/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
+	"regexp"
 )
 
 var ExtractApiPullRequestsMeta = core.SubTaskMeta{
@@ -73,14 +71,14 @@ func ExtractApiPullRequests(taskCtx core.SubTaskContext) error {
 	if len(prType) > 0 {
 		labelTypeRegex, err = regexp.Compile(prType)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prType failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prType failed")
 		}
 	}
 	var prComponent = config.PrComponent
 	if len(prComponent) > 0 {
 		labelComponentRegex, err = regexp.Compile(prComponent)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prComponent failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prComponent failed")
 		}
 	}
 
diff --git a/plugins/github/tasks/pr_issue_enricher.go b/plugins/github/tasks/pr_issue_enricher.go
index fc9c5ab2..4d13872b 100644
--- a/plugins/github/tasks/pr_issue_enricher.go
+++ b/plugins/github/tasks/pr_issue_enricher.go
@@ -18,10 +18,9 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 	"regexp"
-	"runtime/debug"
 	"strconv"
 	"strings"
 
@@ -52,7 +51,7 @@ func EnrichPullRequestIssues(taskCtx core.SubTaskContext) (err error) {
 	if len(prBodyClosePattern) > 0 {
 		prBodyCloseRegex, err = regexp.Compile(prBodyClosePattern)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prBodyClosePattern failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prBodyClosePattern failed")
 		}
 	}
 	charPattern := regexp.MustCompile(`[\/a-zA-Z\s,]+`)
diff --git a/plugins/github/tasks/pr_review_collector.go b/plugins/github/tasks/pr_review_collector.go
index 09c5a0b5..9033683e 100644
--- a/plugins/github/tasks/pr_review_collector.go
+++ b/plugins/github/tasks/pr_review_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"github.com/apache/incubator-devlake/plugins/github/models"
 	"net/http"
@@ -63,7 +64,7 @@ func CollectApiPullRequestReviews(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest github issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest github issue record")
 		}
 		if latestUpdatedPrReview.GithubId > 0 {
 			since = latestUpdatedPrReview.GithubSubmitAt
diff --git a/plugins/github/tasks/pr_review_comment_collector.go b/plugins/github/tasks/pr_review_comment_collector.go
index 7551d98b..2e3240d9 100644
--- a/plugins/github/tasks/pr_review_comment_collector.go
+++ b/plugins/github/tasks/pr_review_comment_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"github.com/apache/incubator-devlake/plugins/github/models"
@@ -53,7 +54,7 @@ func CollectPrReviewComments(taskCtx core.SubTaskContext) error {
 			dal.Limit(1),
 		)
 		if err != nil {
-			return fmt.Errorf("failed to get latest github issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest github issue record")
 		}
 		if latestUpdatedPrReviewComt.GithubId > 0 {
 			since = &latestUpdatedPrReviewComt.GithubUpdatedAt
diff --git a/plugins/github/tasks/pr_review_comment_extractor.go b/plugins/github/tasks/pr_review_comment_extractor.go
index be99ebc3..d6bc7081 100644
--- a/plugins/github/tasks/pr_review_comment_extractor.go
+++ b/plugins/github/tasks/pr_review_comment_extractor.go
@@ -20,10 +20,10 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"gorm.io/gorm"
 	"regexp"
-	"runtime/debug"
 	"strconv"
 
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -86,14 +86,14 @@ func ExtractApiPrReviewComments(taskCtx core.SubTaskContext) error {
 
 			prUrlRegex, err := regexp.Compile(prUrlPattern)
 			if err != nil {
-				return nil, fmt.Errorf("regexp Compile prUrlPattern failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+				return nil, errors.Default.Wrap(err, "regexp Compile prUrlPattern failed")
 			}
 			if prUrlRegex != nil {
 				groups := prUrlRegex.FindStringSubmatch(prReviewComment.PrUrl)
 				if len(groups) > 0 {
 					prNumber, err := strconv.Atoi(groups[1])
 					if err != nil {
-						return nil, fmt.Errorf("parse prId failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+						return nil, errors.Default.Wrap(err, "parse prId failed")
 					}
 					pr := &models.GithubPullRequest{}
 					err = taskCtx.GetDal().First(pr, dal.Where("connection_id = ? and number = ? and repo_id = ?", data.Options.ConnectionId, prNumber, data.Repo.GithubId))
diff --git a/plugins/github/tasks/repo_extractor.go b/plugins/github/tasks/repo_extractor.go
index e5ab717e..eb0f2dc6 100644
--- a/plugins/github/tasks/repo_extractor.go
+++ b/plugins/github/tasks/repo_extractor.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/github/models"
@@ -75,7 +76,7 @@ func ExtractApiRepositories(taskCtx core.SubTaskContext) error {
 				return nil, err
 			}
 			if body.GithubId == 0 {
-				return nil, fmt.Errorf("repo %s/%s not found", data.Options.Owner, data.Options.Repo)
+				return nil, errors.NotFound.New(fmt.Sprintf("repo %s/%s not found", data.Options.Owner, data.Options.Repo))
 			}
 			results := make([]interface{}, 0, 1)
 			githubRepository := &models.GithubRepo{
diff --git a/plugins/github/tasks/task_data.go b/plugins/github/tasks/task_data.go
index 14b5980e..723546cf 100644
--- a/plugins/github/tasks/task_data.go
+++ b/plugins/github/tasks/task_data.go
@@ -18,7 +18,7 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/plugins/github/models"
@@ -50,10 +50,10 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*GithubOption
 		return nil, err
 	}
 	if op.Owner == "" {
-		return nil, fmt.Errorf("owner is required for GitHub execution")
+		return nil, errors.BadInput.New("owner is required for GitHub execution", errors.AsUserMessage())
 	}
 	if op.Repo == "" {
-		return nil, fmt.Errorf("repo is required for GitHub execution")
+		return nil, errors.BadInput.New("repo is required for GitHub execution", errors.AsUserMessage())
 	}
 	if op.PrType == "" {
 		op.PrType = "type/(.*)$"
@@ -85,7 +85,7 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*GithubOption
 
 	// find the needed GitHub now
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.BadInput.New("connectionId is invalid", errors.AsUserMessage())
 	}
 	return &op, nil
 }
diff --git a/plugins/github/utils/utils.go b/plugins/github/utils/utils.go
index 91751b30..59366c0f 100644
--- a/plugins/github/utils/utils.go
+++ b/plugins/github/utils/utils.go
@@ -18,7 +18,8 @@ limitations under the License.
 package utils
 
 import (
-	"errors"
+	//"errors"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"regexp"
 	"strconv"
@@ -48,7 +49,7 @@ func ConvertRateLimitInfo(date string, resetTime string, remaining string) (Rate
 			return rateLimitInfo, err
 		}
 	} else {
-		return rateLimitInfo, errors.New("rate limit date was an empty string")
+		return rateLimitInfo, errors.Default.New("rate limit date was an empty string")
 	}
 	if resetTime != "" {
 		resetInt, err := strconv.ParseInt(resetTime, 10, 64)
@@ -57,7 +58,7 @@ func ConvertRateLimitInfo(date string, resetTime string, remaining string) (Rate
 		}
 		rateLimitInfo.ResetTime = time.Unix(resetInt, 0)
 	} else {
-		return rateLimitInfo, errors.New("rate limit reset time was an empty string")
+		return rateLimitInfo, errors.Default.New("rate limit reset time was an empty string")
 	}
 	if remaining != "" {
 		rateLimitInfo.Remaining, err = strconv.Atoi(remaining)
@@ -65,7 +66,7 @@ func ConvertRateLimitInfo(date string, resetTime string, remaining string) (Rate
 			return rateLimitInfo, err
 		}
 	} else {
-		return rateLimitInfo, errors.New("rate remaining was an empty string")
+		return rateLimitInfo, errors.Default.New("rate remaining was an empty string")
 	}
 	return rateLimitInfo, nil
 }
@@ -93,7 +94,7 @@ func GetPagingFromLinkHeader(link string) (PagingInfo, error) {
 	pattern1 := regexp.MustCompile(`page=*[0-9]+`)
 	pattern2 := regexp.MustCompile(`rel="*[a-z]+`)
 	if len(linksArray) < 2 {
-		return result, errors.New("the link string provided is invalid. There is likely no next page of data to fetch")
+		return result, errors.Default.New("the link string provided is invalid. There is likely no next page of data to fetch")
 	}
 	for i := 0; i < len(linksArray); i++ {
 		content := []byte(linksArray[i])
@@ -120,7 +121,7 @@ func GetPagingFromLinkHeader(link string) (PagingInfo, error) {
 				result.Prev = pageNumberInt
 			}
 		} else {
-			return result, errors.New("parsed string values aren't long enough")
+			return result, errors.Default.New("parsed string values aren't long enough")
 		}
 	}
 	return result, nil
@@ -130,7 +131,7 @@ func GetIssueIdByIssueUrl(s string) (int, error) {
 	regex := regexp.MustCompile(`.*/issues/(\d+)`)
 	groups := regex.FindStringSubmatch(s)
 	if len(groups) == 0 {
-		return 0, errors.New("invalid issue url")
+		return 0, errors.Default.New("invalid issue url")
 	}
 	return strconv.Atoi(groups[1])
 }
diff --git a/plugins/github_graphql/plugin_main.go b/plugins/github_graphql/plugin_main.go
index acd6b20e..fc582d03 100755
--- a/plugins/github_graphql/plugin_main.go
+++ b/plugins/github_graphql/plugin_main.go
@@ -19,7 +19,7 @@ package main
 
 import (
 	"context"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/github/models"
 	githubTasks "github.com/apache/incubator-devlake/plugins/github/tasks"
@@ -90,10 +90,10 @@ func (plugin GithubGraphql) PrepareTaskData(taskCtx core.TaskContext, options ma
 		return nil, err
 	}
 	if op.Owner == "" {
-		return nil, fmt.Errorf("owner is required for GitHub execution")
+		return nil, errors.Default.New("owner is required for GitHub execution")
 	}
 	if op.Repo == "" {
-		return nil, fmt.Errorf("repo is required for GitHub execution")
+		return nil, errors.Default.New("repo is required for GitHub execution")
 	}
 
 	connectionHelper := helper.NewConnectionHelper(
@@ -103,7 +103,7 @@ func (plugin GithubGraphql) PrepareTaskData(taskCtx core.TaskContext, options ma
 	connection := &models.GithubConnection{}
 	err = connectionHelper.FirstById(connection, op.ConnectionId)
 	if err != nil {
-		return nil, fmt.Errorf("unable to get github connection by the given connection ID: %v", err)
+		return nil, errors.Default.Wrap(err, "unable to get github connection by the given connection ID: %v")
 	}
 
 	tokens := strings.Split(connection.Token, ",")
@@ -131,7 +131,7 @@ func (plugin GithubGraphql) PrepareTaskData(taskCtx core.TaskContext, options ma
 
 	apiClient, err := githubTasks.CreateApiClient(taskCtx, connection)
 	if err != nil {
-		return nil, fmt.Errorf("unable to get github API client instance: %v", err)
+		return nil, errors.Default.Wrap(err, "unable to get github API client instance")
 	}
 
 	return &githubTasks.GithubTaskData{
diff --git a/plugins/github_graphql/tasks/pr_collector.go b/plugins/github_graphql/tasks/pr_collector.go
index 08ec5ed5..4afe4d67 100755
--- a/plugins/github_graphql/tasks/pr_collector.go
+++ b/plugins/github_graphql/tasks/pr_collector.go
@@ -18,14 +18,13 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/github/models"
 	githubTasks "github.com/apache/incubator-devlake/plugins/github/tasks"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/merico-dev/graphql"
 	"regexp"
-	"runtime/debug"
 	"time"
 )
 
@@ -133,14 +132,14 @@ func CollectPr(taskCtx core.SubTaskContext) error {
 	if len(prType) > 0 {
 		labelTypeRegex, err = regexp.Compile(prType)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prType failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prType failed")
 		}
 	}
 	var prComponent = config.PrComponent
 	if len(prComponent) > 0 {
 		labelComponentRegex, err = regexp.Compile(prComponent)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prComponent failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prComponent failed")
 		}
 	}
 
diff --git a/plugins/gitlab/api/blueprint.go b/plugins/gitlab/api/blueprint.go
index d328bd7e..0bd266a5 100644
--- a/plugins/gitlab/api/blueprint.go
+++ b/plugins/gitlab/api/blueprint.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -118,10 +119,7 @@ func MakePipelinePlan(subtaskMetas []core.SubTaskMeta, connectionId uint64, scop
 			}
 			defer res.Body.Close()
 			if res.StatusCode != http.StatusOK {
-				return nil, fmt.Errorf(
-					"unexpected status code when requesting repo detail %d %s",
-					res.StatusCode, res.Request.URL.String(),
-				)
+				return nil, errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("unexpected status code when requesting repo detail from %s", res.Request.URL.String()))
 			}
 			body, err := ioutil.ReadAll(res.Body)
 			if err != nil {
diff --git a/plugins/gitlab/api/connection.go b/plugins/gitlab/api/connection.go
index 22b1a539..957fe682 100644
--- a/plugins/gitlab/api/connection.go
+++ b/plugins/gitlab/api/connection.go
@@ -20,6 +20,7 @@ package api
 import (
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"time"
 
@@ -43,12 +44,12 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	var connection models.TestConnectionRequest
 	err = mapstructure.Decode(input.Body, &connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	// validate
 	err = vld.Struct(connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not validate request parameters", errors.AsUserMessage())
 	}
 	// test connection
 	apiClient, err := helper.NewApiClient(
@@ -76,7 +77,7 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	}
 
 	if res.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+		return nil, errors.HttpStatus(res.StatusCode).New("unexpected status code while testing connection")
 	}
 	return nil, nil
 }
diff --git a/plugins/gitlab/impl/impl.go b/plugins/gitlab/impl/impl.go
index 514d47bb..99086a22 100644
--- a/plugins/gitlab/impl/impl.go
+++ b/plugins/gitlab/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/apache/incubator-devlake/migration"
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -111,7 +112,7 @@ func (plugin Gitlab) PrepareTaskData(taskCtx core.TaskContext, options map[strin
 		return nil, err
 	}
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.BadInput.New("connectionId is invalid", errors.AsUserMessage())
 	}
 	connection := &models.GitlabConnection{}
 	connectionHelper := helper.NewConnectionHelper(
@@ -172,7 +173,7 @@ func (plugin Gitlab) ApiResources() map[string]map[string]core.ApiResourceHandle
 func (plugin Gitlab) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.GitlabTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/gitlab/models/migrationscripts/20220714_add_init_tables.go b/plugins/gitlab/models/migrationscripts/20220714_add_init_tables.go
index 3321d260..d60d009c 100644
--- a/plugins/gitlab/models/migrationscripts/20220714_add_init_tables.go
+++ b/plugins/gitlab/models/migrationscripts/20220714_add_init_tables.go
@@ -19,7 +19,6 @@ package migrationscripts
 
 import (
 	"context"
-
 	"github.com/apache/incubator-devlake/config"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitlab/models/migrationscripts/archived"
diff --git a/plugins/gitlab/tasks/account_collector.go b/plugins/gitlab/tasks/account_collector.go
index 9d9c30fb..352186f0 100644
--- a/plugins/gitlab/tasks/account_collector.go
+++ b/plugins/gitlab/tasks/account_collector.go
@@ -65,7 +65,7 @@ func CollectAccounts(taskCtx core.SubTaskContext) error {
 	})
 
 	if err != nil {
-		logger.Error("collect user error:", err)
+		logger.Error(err, "collect user error")
 		return err
 	}
 
diff --git a/plugins/gitlab/tasks/account_extractor.go b/plugins/gitlab/tasks/account_extractor.go
index d2e9c934..311bea42 100644
--- a/plugins/gitlab/tasks/account_extractor.go
+++ b/plugins/gitlab/tasks/account_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitlab/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/gitlab/tasks/api_client.go b/plugins/gitlab/tasks/api_client.go
index 804f2ba4..7ea25151 100644
--- a/plugins/gitlab/tasks/api_client.go
+++ b/plugins/gitlab/tasks/api_client.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"strconv"
 	"time"
@@ -50,7 +51,7 @@ func NewGitlabApiClient(taskCtx core.TaskContext, connection *models.GitlabConne
 			}
 			rateLimit, err := strconv.Atoi(rateLimitHeader)
 			if err != nil {
-				return 0, 0, fmt.Errorf("failed to parse RateLimit-Limit header: %w", err)
+				return 0, 0, errors.Default.Wrap(err, "failed to parse RateLimit-Limit header")
 			}
 			// seems like gitlab rate limit is on minute basis
 			return rateLimit, 1 * time.Minute, nil
diff --git a/plugins/gitlab/tasks/commit_extractor.go b/plugins/gitlab/tasks/commit_extractor.go
index 76e56d58..9321c455 100644
--- a/plugins/gitlab/tasks/commit_extractor.go
+++ b/plugins/gitlab/tasks/commit_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitlab/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/gitlab/tasks/issue_collector.go b/plugins/gitlab/tasks/issue_collector.go
index d71542b1..b5d81f42 100644
--- a/plugins/gitlab/tasks/issue_collector.go
+++ b/plugins/gitlab/tasks/issue_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/http"
 	"net/url"
@@ -54,7 +55,7 @@ func CollectApiIssues(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clause...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest gitlab issue record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest gitlab issue record")
 		}
 		if latestUpdated.GitlabId > 0 {
 			since = &latestUpdated.GitlabUpdatedAt
diff --git a/plugins/gitlab/tasks/issue_extractor.go b/plugins/gitlab/tasks/issue_extractor.go
index d7547ed9..0277f5ae 100644
--- a/plugins/gitlab/tasks/issue_extractor.go
+++ b/plugins/gitlab/tasks/issue_extractor.go
@@ -19,11 +19,9 @@ package tasks
 
 import (
 	"encoding/json"
-	"fmt"
-	"regexp"
-	"runtime/debug"
-
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/models/domainlayer/ticket"
+	"regexp"
 
 	"github.com/apache/incubator-devlake/plugins/core"
 
@@ -143,42 +141,42 @@ func ExtractApiIssues(taskCtx core.SubTaskContext) error {
 	if len(issueSeverity) > 0 {
 		issueSeverityRegex, err = regexp.Compile(issueSeverity)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueSeverity failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueSeverity failed")
 		}
 	}
 	var issueComponent = config.IssueComponent
 	if len(issueComponent) > 0 {
 		issueComponentRegex, err = regexp.Compile(issueComponent)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueComponent failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueComponent failed")
 		}
 	}
 	var issuePriority = config.IssuePriority
 	if len(issuePriority) > 0 {
 		issuePriorityRegex, err = regexp.Compile(issuePriority)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issuePriority failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issuePriority failed")
 		}
 	}
 	var issueTypeBug = config.IssueTypeBug
 	if len(issueTypeBug) > 0 {
 		issueTypeBugRegex, err = regexp.Compile(issueTypeBug)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueTypeBug failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueTypeBug failed")
 		}
 	}
 	var issueTypeRequirement = config.IssueTypeRequirement
 	if len(issueTypeRequirement) > 0 {
 		issueTypeRequirementRegex, err = regexp.Compile(issueTypeRequirement)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueTypeRequirement failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueTypeRequirement failed")
 		}
 	}
 	var issueTypeIncident = config.IssueTypeIncident
 	if len(issueTypeIncident) > 0 {
 		issueTypeIncidentRegex, err = regexp.Compile(issueTypeIncident)
 		if err != nil {
-			return fmt.Errorf("regexp Compile issueTypeIncident failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile issueTypeIncident failed")
 		}
 	}
 	extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
diff --git a/plugins/gitlab/tasks/job_extractor.go b/plugins/gitlab/tasks/job_extractor.go
index b67381cd..6e978edb 100644
--- a/plugins/gitlab/tasks/job_extractor.go
+++ b/plugins/gitlab/tasks/job_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitlab/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/gitlab/tasks/mr_extractor.go b/plugins/gitlab/tasks/mr_extractor.go
index c165d7bf..6ee996da 100644
--- a/plugins/gitlab/tasks/mr_extractor.go
+++ b/plugins/gitlab/tasks/mr_extractor.go
@@ -19,13 +19,11 @@ package tasks
 
 import (
 	"encoding/json"
-	"fmt"
-	"regexp"
-	"runtime/debug"
-
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitlab/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
+	"regexp"
 )
 
 type MergeRequestRes struct {
@@ -86,14 +84,14 @@ func ExtractApiMergeRequests(taskCtx core.SubTaskContext) error {
 	if len(prType) > 0 {
 		labelTypeRegex, err = regexp.Compile(prType)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prType failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prType failed")
 		}
 	}
 	var prComponent = config.PrComponent
 	if len(prComponent) > 0 {
 		labelComponentRegex, err = regexp.Compile(prComponent)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prComponent failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prComponent failed")
 		}
 	}
 	extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
diff --git a/plugins/gitlab/tasks/pipeline_extractor.go b/plugins/gitlab/tasks/pipeline_extractor.go
index cc783c0d..a3a23543 100644
--- a/plugins/gitlab/tasks/pipeline_extractor.go
+++ b/plugins/gitlab/tasks/pipeline_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitlab/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/gitlab/tasks/project_extractor.go b/plugins/gitlab/tasks/project_extractor.go
index 6f3619c9..cf4b78c1 100644
--- a/plugins/gitlab/tasks/project_extractor.go
+++ b/plugins/gitlab/tasks/project_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitlab/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/gitlab/tasks/shared.go b/plugins/gitlab/tasks/shared.go
index 58ccbbfa..9258889c 100644
--- a/plugins/gitlab/tasks/shared.go
+++ b/plugins/gitlab/tasks/shared.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"io/ioutil"
 	"net/http"
@@ -57,17 +58,17 @@ func GetRawMessageFromResponse(res *http.Response) ([]json.RawMessage, error) {
 	rawMessages := []json.RawMessage{}
 
 	if res == nil {
-		return nil, fmt.Errorf("res is nil")
+		return nil, errors.Default.New("res is nil")
 	}
 	defer res.Body.Close()
 	resBody, err := ioutil.ReadAll(res.Body)
 	if err != nil {
-		return nil, fmt.Errorf("%w %s", err, res.Request.URL.String())
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("error reading response from %s", res.Request.URL.String()))
 	}
 
 	err = json.Unmarshal(resBody, &rawMessages)
 	if err != nil {
-		return nil, fmt.Errorf("%w %s %s", err, res.Request.URL.String(), string(resBody))
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("error decoding response from %s. raw response was: %s", res.Request.URL.String(), string(resBody)))
 	}
 
 	return rawMessages, nil
diff --git a/plugins/gitlab/tasks/tag_extractor.go b/plugins/gitlab/tasks/tag_extractor.go
index 3b933498..3861ab34 100644
--- a/plugins/gitlab/tasks/tag_extractor.go
+++ b/plugins/gitlab/tasks/tag_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/gitlab/models"
 	"github.com/apache/incubator-devlake/plugins/helper"
diff --git a/plugins/gitlab/tasks/task_data.go b/plugins/gitlab/tasks/task_data.go
index 82cbbfd7..4787278b 100644
--- a/plugins/gitlab/tasks/task_data.go
+++ b/plugins/gitlab/tasks/task_data.go
@@ -18,7 +18,7 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/mitchellh/mapstructure"
 	"time"
 
@@ -45,10 +45,10 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*GitlabOption
 	var op GitlabOptions
 	err := mapstructure.Decode(options, &op)
 	if err != nil {
-		return nil, err
+		return nil, errors.Default.WrapRaw(err)
 	}
 	if op.ProjectId == 0 {
-		return nil, fmt.Errorf("ProjectId is required for Gitlab execution")
+		return nil, errors.BadInput.New("ProjectId is required for Gitlab execution", errors.AsUserMessage())
 	}
 	if op.PrType == "" {
 		op.PrType = "type/(.*)$"
@@ -80,7 +80,7 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*GitlabOption
 
 	// find the needed GitHub now
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.BadInput.New("connectionId is invalid", errors.AsUserMessage())
 	}
 	return &op, nil
 }
diff --git a/plugins/helper/api_async_client.go b/plugins/helper/api_async_client.go
index fe60a825..fea1f122 100644
--- a/plugins/helper/api_async_client.go
+++ b/plugins/helper/api_async_client.go
@@ -21,6 +21,7 @@ import (
 	"bytes"
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io"
 	"io/ioutil"
 	"net/http"
@@ -54,18 +55,18 @@ func CreateAsyncApiClient(
 	// load retry/timeout from configuration
 	retry, err := utils.StrToIntOr(taskCtx.GetConfig("API_RETRY"), 3)
 	if err != nil {
-		return nil, fmt.Errorf("failed to parse API_RETRY: %w", err)
+		return nil, errors.BadInput.Wrap(err, "failed to parse API_RETRY", errors.AsUserMessage())
 	}
 	timeout, err := utils.StrToDurationOr(taskCtx.GetConfig("API_TIMEOUT"), 10*time.Second)
 	if err != nil {
-		return nil, fmt.Errorf("failed to parse API_TIMEOUT: %w", err)
+		return nil, errors.BadInput.Wrap(err, "failed to parse API_TIMEOUT", errors.AsUserMessage())
 	}
 	apiClient.SetTimeout(timeout)
 	apiClient.SetLogger(taskCtx.GetLogger())
 
 	globalRateLimitPerHour, err := utils.StrToIntOr(taskCtx.GetConfig("API_REQUESTS_PER_HOUR"), 18000)
 	if err != nil {
-		return nil, fmt.Errorf("failed to parse API_REQUESTS_PER_HOUR: %w", err)
+		return nil, errors.Default.Wrap(err, "failed to parse API_REQUESTS_PER_HOUR")
 	}
 	if rateLimiter == nil {
 		rateLimiter = &ApiRateLimitCalculator{}
@@ -76,7 +77,7 @@ func CreateAsyncApiClient(
 	// ok, calculate api rate limit based on response (normally from headers)
 	requests, duration, err := rateLimiter.Calculate(apiClient)
 	if err != nil {
-		return nil, fmt.Errorf("failed to calculate rateLimit for api: %w", err)
+		return nil, errors.Default.Wrap(err, "failed to calculate rateLimit for api")
 	}
 
 	// it is hard to tell how many workers would be sufficient, it depends on how slow the server responds.
@@ -104,7 +105,7 @@ func CreateAsyncApiClient(
 		logger,
 	)
 	if err != nil {
-		return nil, fmt.Errorf("failed to create scheduler: %w", err)
+		return nil, errors.Default.Wrap(err, "failed to create scheduler")
 	}
 
 	// finally, wrap around api client with async sematic
@@ -168,14 +169,14 @@ func (apiClient *ApiAsyncClient) DoAsync(
 			needRetry = true
 		} else if res.StatusCode >= HttpMinStatusRetryCode {
 			needRetry = true
-			err = fmt.Errorf("http code error[%d]:[%s]", res.StatusCode, body)
+			err = errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("Http DoAsync error: %s", body))
 		}
 
-		//  if it need retry, check and try to retry
+		//  if it needs retry, check and retry
 		if needRetry {
-			// check weather we still have retry times and not error from handler and canceled error
+			// check whether we still have retry times and not error from handler and canceled error
 			if retry < apiClient.maxRetry && err != context.Canceled {
-				apiClient.logger.Warn("retry #%d for %s", retry, err.Error())
+				apiClient.logger.Warn(err, "retry #%d calling %s", retry, path)
 				retry++
 				apiClient.scheduler.NextTick(func() error {
 					apiClient.scheduler.SubmitBlocking(request)
@@ -186,7 +187,8 @@ func (apiClient *ApiAsyncClient) DoAsync(
 		}
 
 		if err != nil {
-			apiClient.logger.Error("retry exceeded times: %d, err: %s", retry, err.Error())
+			err = errors.Default.Wrap(err, fmt.Sprintf("retry exceeded %d times calling %s", retry, path), errors.UserMessage("Async API call retries exhausted"))
+			apiClient.logger.Error(err, "")
 			return err
 		}
 
diff --git a/plugins/helper/api_client.go b/plugins/helper/api_client.go
index 2baf0783..07ab53fc 100644
--- a/plugins/helper/api_client.go
+++ b/plugins/helper/api_client.go
@@ -22,8 +22,8 @@ import (
 	"context"
 	"crypto/tls"
 	"encoding/json"
-	"errors"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io"
 	"io/ioutil"
 	"net/http"
@@ -39,7 +39,7 @@ import (
 )
 
 // ErrIgnoreAndContinue is a error which should be ignored
-var ErrIgnoreAndContinue = errors.New("ignore and continue")
+var ErrIgnoreAndContinue = errors.Default.New("ignore and continue")
 
 // ApiClient is designed for simple api requests
 type ApiClient struct {
@@ -61,25 +61,24 @@ func NewApiClient(
 	proxy string,
 	br core.BasicRes,
 ) (*ApiClient, error) {
-
 	parsedUrl, err := url.Parse(endpoint)
 	if err != nil {
-		return nil, fmt.Errorf("Invalid URL: %w", err)
+		return nil, errors.BadInput.Wrap(err, fmt.Sprintf("Invalid URL: %s", endpoint), errors.AsUserMessage())
 	}
 	if parsedUrl.Scheme == "" {
-		return nil, fmt.Errorf("Invalid schema")
+		return nil, errors.BadInput.New("Invalid URL scheme", errors.AsUserMessage())
 	}
 	err = utils.CheckDNS(parsedUrl.Hostname())
 	if err != nil {
-		return nil, fmt.Errorf("Failed to resolve DNS: %w", err)
+		return nil, errors.Default.Wrap(err, "Failed to resolve DNS", errors.AsUserMessage())
 	}
 	port, err := utils.ResolvePort(parsedUrl.Port(), parsedUrl.Scheme)
 	if err != nil {
-		return nil, fmt.Errorf("Failed to resolve Port: %w", err)
+		return nil, errors.Default.New("Failed to resolve Port", errors.AsUserMessage())
 	}
 	err = utils.CheckNetwork(parsedUrl.Hostname(), port, time.Duration(2)*time.Second)
 	if err != nil {
-		return nil, fmt.Errorf("Failed to connect: %w", err)
+		return nil, errors.Default.Wrap(err, "Failed to connect", errors.AsUserMessage())
 	}
 	apiClient := &ApiClient{}
 	apiClient.Setup(
@@ -93,7 +92,7 @@ func NewApiClient(
 	// set insecureSkipVerify
 	insecureSkipVerify, err := utils.StrToBoolOr(br.GetConfig("IN_SECURE_SKIP_VERIFY"), false)
 	if err != nil {
-		return nil, fmt.Errorf("failt to parse IN_SECURE_SKIP_VERIFY: %w", err)
+		return nil, errors.Default.Wrap(err, "failed to parse IN_SECURE_SKIP_VERIFY", errors.UserMessage("Malformed config on server"))
 	}
 	if insecureSkipVerify {
 		apiClient.client.Transport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
@@ -194,9 +193,9 @@ func (apiClient *ApiClient) logDebug(format string, a ...interface{}) {
 	}
 }
 
-func (apiClient *ApiClient) logError(format string, a ...interface{}) {
+func (apiClient *ApiClient) logError(err error, format string, a ...interface{}) {
 	if apiClient.logger != nil {
-		apiClient.logger.Error(format, a...)
+		apiClient.logger.Error(err, format, a...)
 	}
 }
 
@@ -210,14 +209,14 @@ func (apiClient *ApiClient) Do(
 ) (*http.Response, error) {
 	uri, err := GetURIStringPointer(apiClient.endpoint, path, query)
 	if err != nil {
-		return nil, err
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("Unable to construct URI from %s, %s, %s", apiClient.endpoint, path, query), errors.UserMessage("Unable to construct URI"))
 	}
 	// process body
 	var reqBody io.Reader
 	if body != nil {
 		reqJson, err := json.Marshal(body)
 		if err != nil {
-			return nil, err
+			return nil, errors.Default.Wrap(err, fmt.Sprintf("unable to serialize API request body for %s", *uri), errors.UserMessage("unable to serialize API request body"))
 		}
 		reqBody = bytes.NewBuffer(reqJson)
 	}
@@ -228,7 +227,7 @@ func (apiClient *ApiClient) Do(
 		req, err = http.NewRequest(method, *uri, reqBody)
 	}
 	if err != nil {
-		return nil, err
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("unable to create API request for %s", *uri), errors.UserMessage("unable to create API request"))
 	}
 	req.Header.Set("Content-Type", "application/json")
 
@@ -249,30 +248,24 @@ func (apiClient *ApiClient) Do(
 	if apiClient.beforeRequest != nil {
 		err = apiClient.beforeRequest(req)
 		if err != nil {
-			return nil, err
+			return nil, errors.Default.Wrap(err, fmt.Sprintf("error running beforeRequest for %s", req.URL.String()), errors.UserMessage("error before making API call"))
 		}
 	}
 	apiClient.logDebug("[api-client] %v %v", method, *uri)
 	res, err = apiClient.client.Do(req)
 	if err != nil {
-		apiClient.logError("[api-client] failed to request %s with error:\n%s", req.URL.String(), err.Error())
-		return nil, err
+		apiClient.logError(err, "[api-client] failed to request %s with error", req.URL.String())
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("error running beforeRequest for %s", req.URL.String()), errors.UserMessage("error before making API call"))
 	}
-
 	// after receive
 	if apiClient.afterResponse != nil {
 		err = apiClient.afterResponse(res)
-		if err == ErrIgnoreAndContinue {
-			res.Body.Close()
-			return res, err
-		}
-		if err != nil {
+		if err != nil && err != ErrIgnoreAndContinue {
 			res.Body.Close()
-			return nil, err
+			return nil, errors.Default.Wrap(err, fmt.Sprintf("error running afterRequest for %s", req.URL.String()), errors.UserMessage("error after making API call"))
 		}
 	}
-
-	return res, err
+	return res, nil
 }
 
 // Get FIXME ...
@@ -299,11 +292,11 @@ func UnmarshalResponse(res *http.Response, v interface{}) error {
 	defer res.Body.Close()
 	resBody, err := ioutil.ReadAll(res.Body)
 	if err != nil {
-		return fmt.Errorf("%w %s", err, res.Request.URL.String())
+		return errors.Default.Wrap(err, fmt.Sprintf("error reading response from %s", res.Request.URL.String()), errors.AsUserMessage())
 	}
 	err = json.Unmarshal(resBody, &v)
 	if err != nil {
-		return fmt.Errorf("%w %s %s", err, res.Request.URL.String(), string(resBody))
+		return errors.Default.Wrap(err, fmt.Sprintf("error decoding response from %s: raw response: %s", res.Request.URL.String(), string(resBody)), errors.AsUserMessage())
 	}
 	return nil
 }
diff --git a/plugins/helper/api_collector.go b/plugins/helper/api_collector.go
index 6a4aaaec..e4b7513f 100644
--- a/plugins/helper/api_collector.go
+++ b/plugins/helper/api_collector.go
@@ -20,7 +20,7 @@ package helper
 import (
 	"bytes"
 	"encoding/json"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -93,21 +93,21 @@ func NewApiCollector(args ApiCollectorArgs) (*ApiCollector, error) {
 	// process args
 	rawDataSubTask, err := newRawDataSubTask(args.RawDataSubTaskArgs)
 	if err != nil {
-		return nil, err
+		return nil, errors.Default.Wrap(err, "Couldn't resolve raw subtask args", errors.UserMessage("Internal Collector setup error"))
 	}
 	// TODO: check if args.Table is valid
 	if args.UrlTemplate == "" {
-		return nil, fmt.Errorf("UrlTemplate is required")
+		return nil, errors.Default.New("UrlTemplate is required", errors.UserMessage("Internal Collector setup error"))
 	}
 	tpl, err := template.New(args.Table).Parse(args.UrlTemplate)
 	if err != nil {
-		return nil, fmt.Errorf("Failed to compile UrlTemplate: %w", err)
+		return nil, errors.Default.Wrap(err, "Failed to compile UrlTemplate", errors.UserMessage("Internal Collector setup error"))
 	}
 	if args.ApiClient == nil {
-		return nil, fmt.Errorf("ApiClient is required")
+		return nil, errors.Default.New("ApiClient is required", errors.UserMessage("Internal Collector setup error"))
 	}
 	if args.ResponseParser == nil {
-		return nil, fmt.Errorf("ResponseParser is required")
+		return nil, errors.Default.New("ResponseParser is required", errors.UserMessage("Internal Collector setup error"))
 	}
 	apiCollector := &ApiCollector{
 		RawDataSubTask: rawDataSubTask,
@@ -119,7 +119,7 @@ func NewApiCollector(args ApiCollectorArgs) (*ApiCollector, error) {
 	} else if apiCollector.GetAfterResponse() == nil {
 		apiCollector.SetAfterResponse(func(res *http.Response) error {
 			if res.StatusCode == http.StatusUnauthorized {
-				return fmt.Errorf("authentication failed, please check your AccessToken")
+				return errors.Unauthorized.New("authentication failed, please check your AccessToken", errors.AsUserMessage())
 			}
 			return nil
 		})
@@ -136,27 +136,25 @@ func (collector *ApiCollector) Execute() error {
 	db := collector.args.Ctx.GetDal()
 	err := db.AutoMigrate(&RawData{}, dal.From(collector.table))
 	if err != nil {
-		return err
+		return errors.Default.Wrap(err, "error auto-migrating collector", errors.UserMessage("Internal Collector execution error"))
 	}
 
 	// flush data if not incremental collection
 	if !collector.args.Incremental {
 		err = db.Delete(&RawData{}, dal.From(collector.table), dal.Where("params = ?", collector.params))
 		if err != nil {
-			return err
+			return errors.Default.Wrap(err, "error deleting data from collector", errors.UserMessage("Internal Collector execution error"))
 		}
 	}
 
 	collector.args.Ctx.SetProgress(0, -1)
 	if collector.args.Input != nil {
-
 		iterator := collector.args.Input
 		defer iterator.Close()
 		apiClient := collector.args.ApiClient
 		if apiClient == nil {
-			return fmt.Errorf("api_collector can not Execute with nil apiClient")
+			return errors.Default.New("api_collector can not Execute with nil apiClient", errors.UserMessage("Internal Collector execution error"))
 		}
-
 		for {
 			if !iterator.HasNext() || apiClient.HasError() {
 				collector.args.ApiClient.WaitAsync()
@@ -176,12 +174,13 @@ func (collector *ApiCollector) Execute() error {
 	}
 
 	if err != nil {
-		return err
+		return errors.Default.Wrap(err, "error executing collector", errors.UserMessage("Internal Collector execution error"))
 	}
 	logger.Debug("wait for all async api to be finished")
 	err = collector.args.ApiClient.WaitAsync()
 	if err != nil {
-		logger.Info("end api collection error: %w", err)
+		logger.Error(err, "end api collection error")
+		err = errors.Default.Wrap(err, "Error waiting for async Collector execution", errors.AsUserMessage())
 	} else {
 		logger.Info("end api collection without error")
 	}
@@ -216,7 +215,7 @@ func (collector *ApiCollector) fetchPagesDetermined(reqData *RequestData) {
 	collector.fetchAsync(reqData, func(count int, body []byte, res *http.Response) error {
 		totalPages, err := collector.args.GetTotalPages(res, collector.args)
 		if err != nil {
-			return fmt.Errorf("fetchPagesDetermined get totalPages faileds: %s", err.Error())
+			return errors.Default.Wrap(err, "fetchPagesDetermined get totalPages failed")
 		}
 		// spawn a none blocking go routine to fetch other pages
 		collector.args.ApiClient.NextTick(func() error {
diff --git a/plugins/helper/api_collector_func.go b/plugins/helper/api_collector_func.go
index 3534306f..34a8343e 100644
--- a/plugins/helper/api_collector_func.go
+++ b/plugins/helper/api_collector_func.go
@@ -20,6 +20,7 @@ package helper
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/ioutil"
 	"net/http"
 )
@@ -39,17 +40,17 @@ func GetRawMessageArrayFromResponse(res *http.Response) ([]json.RawMessage, erro
 	rawMessages := []json.RawMessage{}
 
 	if res == nil {
-		return nil, fmt.Errorf("res is nil")
+		return nil, errors.Default.New("res is nil")
 	}
 	defer res.Body.Close()
 	resBody, err := ioutil.ReadAll(res.Body)
 	if err != nil {
-		return nil, fmt.Errorf("%w %s", err, res.Request.URL.String())
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("error reading response body of %s", res.Request.URL.String()))
 	}
 
 	err = json.Unmarshal(resBody, &rawMessages)
 	if err != nil {
-		return nil, fmt.Errorf("%w %s %s", err, res.Request.URL.String(), string(resBody))
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("error decoding response of %s: raw response: %s", res.Request.URL.String(), string(resBody)))
 	}
 
 	return rawMessages, nil
diff --git a/plugins/helper/api_extractor.go b/plugins/helper/api_extractor.go
index 4d58c076..0cb798c9 100644
--- a/plugins/helper/api_extractor.go
+++ b/plugins/helper/api_extractor.go
@@ -18,6 +18,7 @@ limitations under the License.
 package helper
 
 import (
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 
 	"github.com/apache/incubator-devlake/models/common"
@@ -74,11 +75,11 @@ func (extractor *ApiExtractor) Execute() error {
 
 	count, err := db.Count(clauses...)
 	if err != nil {
-		return err
+		return errors.Default.Wrap(err, "error getting count of clauses", errors.UserMessage("Internal Extractor execution error"))
 	}
 	cursor, err := db.Cursor(clauses...)
 	if err != nil {
-		return err
+		return errors.Default.Wrap(err, "error running DB query", errors.UserMessage("Internal Extractor execution error"))
 	}
 	log.Info("get data from %s where params=%s and got %d", extractor.table, extractor.params, count)
 	defer cursor.Close()
@@ -100,19 +101,19 @@ func (extractor *ApiExtractor) Execute() error {
 		}
 		err = db.Fetch(cursor, row)
 		if err != nil {
-			return err
+			return errors.Default.Wrap(err, "error fetching row", errors.UserMessage("Internal Extractor execution error"))
 		}
 
 		results, err := extractor.args.Extract(row)
 		if err != nil {
-			return err
+			return errors.Default.Wrap(err, "error calling plugin Extract implementation", errors.UserMessage("Internal Extractor execution error"))
 		}
 
 		for _, result := range results {
 			// get the batch operator for the specific type
 			batch, err := divider.ForType(reflect.TypeOf(result))
 			if err != nil {
-				return err
+				return errors.Default.Wrap(err, "error getting batch from result", errors.UserMessage("Internal Extractor execution error"))
 			}
 			// set raw data origin field
 			origin := reflect.ValueOf(result).Elem().FieldByName(RAW_DATA_ORIGIN)
@@ -126,7 +127,7 @@ func (extractor *ApiExtractor) Execute() error {
 			// records get saved into db when slots were max outed
 			err = batch.Add(result)
 			if err != nil {
-				return err
+				return errors.Default.Wrap(err, "error adding result to batch", errors.UserMessage("Internal Extractor execution error"))
 			}
 			extractor.args.Ctx.IncProgress(1)
 		}
diff --git a/plugins/helper/api_rawdata.go b/plugins/helper/api_rawdata.go
index a01f9695..5c74cf01 100644
--- a/plugins/helper/api_rawdata.go
+++ b/plugins/helper/api_rawdata.go
@@ -20,6 +20,7 @@ package helper
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -57,16 +58,16 @@ type RawDataSubTask struct {
 
 func newRawDataSubTask(args RawDataSubTaskArgs) (*RawDataSubTask, error) {
 	if args.Ctx == nil {
-		return nil, fmt.Errorf("Ctx is required for RawDataSubTask")
+		return nil, errors.Default.New("Ctx is required for RawDataSubTask")
 	}
 	if args.Table == "" {
-		return nil, fmt.Errorf("Table is required for RawDataSubTask")
+		return nil, errors.Default.New("Table is required for RawDataSubTask")
 	}
 	paramsString := ""
 	if args.Params == nil {
-		args.Ctx.GetLogger().Warn("Missing `Params` for raw data subtask %s", args.Ctx.GetName())
+		args.Ctx.GetLogger().Warn(nil, "Missing `Params` for raw data subtask %s", args.Ctx.GetName())
 	} else {
-		// TODO: maybe sort it to make it consisitence
+		// TODO: maybe sort it to make it consistent
 		paramsBytes, err := json.Marshal(args.Params)
 		if err != nil {
 			return nil, err
diff --git a/plugins/helper/batch_save.go b/plugins/helper/batch_save.go
index 6aa9840d..c76b00ac 100644
--- a/plugins/helper/batch_save.go
+++ b/plugins/helper/batch_save.go
@@ -19,6 +19,7 @@ package helper
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 	"strings"
 
@@ -44,13 +45,13 @@ type BatchSave struct {
 // NewBatchSave creates a new BatchSave instance
 func NewBatchSave(basicRes core.BasicRes, slotType reflect.Type, size int) (*BatchSave, error) {
 	if slotType.Kind() != reflect.Ptr {
-		return nil, fmt.Errorf("slotType must be a pointer")
+		return nil, errors.Default.New("slotType must be a pointer")
 	}
 	db := basicRes.GetDal()
 	primaryKey := db.GetPrimaryKeyFields(slotType)
 	// check if it have primaryKey
 	if len(primaryKey) == 0 {
-		return nil, fmt.Errorf("%s no primary key", slotType.String())
+		return nil, errors.Default.New(fmt.Sprintf("%s no primary key", slotType.String()))
 	}
 
 	log := basicRes.GetLogger().Nested(slotType.String())
@@ -70,10 +71,10 @@ func NewBatchSave(basicRes core.BasicRes, slotType reflect.Type, size int) (*Bat
 func (c *BatchSave) Add(slot interface{}) error {
 	// type checking
 	if reflect.TypeOf(slot) != c.slotType {
-		return fmt.Errorf("sub cache type mismatched")
+		return errors.Default.New("sub cache type mismatched")
 	}
 	if reflect.ValueOf(slot).Kind() != reflect.Ptr {
-		return fmt.Errorf("slot is not a pointer")
+		return errors.Default.New("slot is not a pointer")
 	}
 	// deduplication
 	key := getKeyValue(slot, c.primaryKey)
diff --git a/plugins/helper/batch_save_divider.go b/plugins/helper/batch_save_divider.go
index 6ec3c626..57549a10 100644
--- a/plugins/helper/batch_save_divider.go
+++ b/plugins/helper/batch_save_divider.go
@@ -19,6 +19,7 @@ package helper
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 
 	"github.com/apache/incubator-devlake/models/common"
@@ -71,7 +72,7 @@ func (d *BatchSaveDivider) ForType(rowType reflect.Type) (*BatchSave, error) {
 		// check if rowType had RawDataOrigin embeded
 		field, hasField := rowElemType.FieldByName("RawDataOrigin")
 		if !hasField || field.Type != reflect.TypeOf(common.RawDataOrigin{}) {
-			return nil, fmt.Errorf("type %s must have RawDataOrigin embeded", rowElemType.Name())
+			return nil, errors.Default.New(fmt.Sprintf("type %s must have RawDataOrigin embeded", rowElemType.Name()))
 		}
 		// all good, delete outdated records before we insertion
 		d.log.Debug("deleting outdate records for %s", rowElemType.Name())
diff --git a/plugins/helper/config_util.go b/plugins/helper/config_util.go
index ee51d007..b2131812 100644
--- a/plugins/helper/config_util.go
+++ b/plugins/helper/config_util.go
@@ -19,6 +19,7 @@ package helper
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 
 	"github.com/apache/incubator-devlake/utils"
@@ -49,7 +50,7 @@ func DecodeStruct(output *viper.Viper, input interface{}, data map[string]interf
 
 	vf := reflect.ValueOf(input)
 	if vf.Kind() != reflect.Ptr {
-		return fmt.Errorf("input %v is not a pointer", input)
+		return errors.Default.New(fmt.Sprintf("input %v is not a pointer", input))
 	}
 
 	for _, f := range utils.WalkFields(reflect.Indirect(vf).Type(), nil) {
@@ -108,7 +109,7 @@ func DecodeStruct(output *viper.Viper, input interface{}, data map[string]interf
 func EncodeStruct(input *viper.Viper, output interface{}, tag string) error {
 	vf := reflect.ValueOf(output)
 	if vf.Kind() != reflect.Ptr {
-		return fmt.Errorf("output %v is not a pointer", output)
+		return errors.Default.New(fmt.Sprintf("output %v is not a pointer", output))
 	}
 
 	for _, f := range utils.WalkFields(reflect.Indirect(vf).Type(), nil) {
diff --git a/plugins/helper/connection.go b/plugins/helper/connection.go
index 88a4af99..27e6e708 100644
--- a/plugins/helper/connection.go
+++ b/plugins/helper/connection.go
@@ -20,6 +20,7 @@ package helper
 import (
 	"encoding/base64"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 	"strconv"
 
@@ -118,11 +119,11 @@ func (c *ConnectionApiHelper) Patch(connection interface{}, input *core.ApiResou
 func (c *ConnectionApiHelper) First(connection interface{}, params map[string]string) error {
 	connectionId := params["connectionId"]
 	if connectionId == "" {
-		return fmt.Errorf("missing connectionId")
+		return errors.BadInput.New("missing connectionId", errors.AsUserMessage())
 	}
 	id, err := strconv.ParseUint(connectionId, 10, 64)
 	if err != nil || id < 1 {
-		return fmt.Errorf("invalid connectionId")
+		return errors.BadInput.New("invalid connectionId", errors.AsUserMessage())
 	}
 	return c.FirstById(connection, id)
 }
@@ -187,7 +188,7 @@ func (c *ConnectionApiHelper) decrypt(connection interface{}) {
 		return core.Decrypt(c.encKey, encrypted)
 	})
 	if err != nil {
-		c.log.Error("failed to decrypt: %w", err)
+		c.log.Error(err, "failed to decrypt")
 	}
 }
 
@@ -196,7 +197,7 @@ func (c *ConnectionApiHelper) encrypt(connection interface{}) {
 		return core.Encrypt(c.encKey, plaintext)
 	})
 	if err != nil {
-		c.log.Error("failed to encrypt: %w", err)
+		c.log.Error(err, "failed to encrypt")
 	}
 }
 
@@ -204,11 +205,11 @@ func (c *ConnectionApiHelper) encrypt(connection interface{}) {
 func UpdateEncryptFields(val interface{}, update func(in string) (string, error)) error {
 	v := reflect.ValueOf(val)
 	if v.Kind() != reflect.Ptr {
-		panic(fmt.Errorf("val is not a pointer: %v", val))
+		panic(errors.Default.New(fmt.Sprintf("val is not a pointer: %v", val)))
 	}
 	e := v.Elem()
 	if e.Kind() != reflect.Struct {
-		panic(fmt.Errorf("*val is not a struct: %v", val))
+		panic(errors.Default.New(fmt.Sprintf("*val is not a struct: %v", val)))
 	}
 	t := e.Type()
 	for i := 0; i < t.NumField(); i++ {
diff --git a/plugins/helper/data_convertor.go b/plugins/helper/data_convertor.go
index 626f5bda..329b9528 100644
--- a/plugins/helper/data_convertor.go
+++ b/plugins/helper/data_convertor.go
@@ -19,6 +19,7 @@ package helper
 
 import (
 	"database/sql"
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -29,12 +30,13 @@ type DataConvertHandler func(row interface{}) ([]interface{}, error)
 
 // DataConverterArgs includes the arguments about DataConverter.
 // This will be used in Creating a DataConverter.
-// DataConverterArgs {
-//			InputRowType: 		type of inputRow ,
-//			Input:        		dal cursor,
-//			RawDataSubTaskArgs: args about raw data task
-//			Convert: 			main function including conversion logic
-//			BatchSize: 			batch size
+//
+//	DataConverterArgs {
+//				InputRowType: 		type of inputRow ,
+//				Input:        		dal cursor,
+//				RawDataSubTaskArgs: args about raw data task
+//				Convert: 			main function including conversion logic
+//				BatchSize: 			batch size
 type DataConverterArgs struct {
 	RawDataSubTaskArgs
 	// Domain layer entity Id prefix, i.e. `jira:JiraIssue:1`, `github:GithubIssue`
@@ -98,19 +100,19 @@ func (converter *DataConverter) Execute() error {
 		inputRow := reflect.New(converter.args.InputRowType).Interface()
 		err := db.Fetch(cursor, inputRow)
 		if err != nil {
-			return err
+			return errors.Default.Wrap(err, "error fetching rows", errors.UserMessage("Internal Converter execution error"))
 		}
 
 		results, err := converter.args.Convert(inputRow)
 		if err != nil {
-			return err
+			return errors.Default.Wrap(err, "error calling Converter plugin implementation", errors.UserMessage("Internal Converter execution error"))
 		}
 
 		for _, result := range results {
 			// get the batch operator for the specific type
 			batch, err := divider.ForType(reflect.TypeOf(result))
 			if err != nil {
-				return err
+				return errors.Default.Wrap(err, "error getting batch from result", errors.UserMessage("Internal Converter execution error"))
 			}
 			// set raw data origin field
 			origin := reflect.ValueOf(result).Elem().FieldByName(RAW_DATA_ORIGIN)
@@ -120,7 +122,7 @@ func (converter *DataConverter) Execute() error {
 			// records get saved into db when slots were max outed
 			err = batch.Add(result)
 			if err != nil {
-				return err
+				return errors.Default.Wrap(err, "error adding result to batch", errors.UserMessage("Internal Converter execution error"))
 			}
 		}
 		converter.args.Ctx.IncProgress(1)
@@ -130,5 +132,5 @@ func (converter *DataConverter) Execute() error {
 	return divider.Close()
 }
 
-//Check if DataConverter implements SubTask interface
+// Check if DataConverter implements SubTask interface
 var _ core.SubTask = (*DataConverter)(nil)
diff --git a/plugins/helper/default_task_context.go b/plugins/helper/default_task_context.go
index 512a40aa..e363c960 100644
--- a/plugins/helper/default_task_context.go
+++ b/plugins/helper/default_task_context.go
@@ -20,6 +20,7 @@ package helper
 import (
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -248,7 +249,7 @@ func (c *DefaultTaskContext) SubTaskContext(subtask string) (core.SubTaskContext
 		return nil, nil
 	}
 	// invalid subtask name
-	return nil, fmt.Errorf("subtask %s doesn't exist", subtask)
+	return nil, errors.Default.New(fmt.Sprintf("subtask %s doesn't exist", subtask))
 }
 
 // NewStandaloneSubTaskContext returns a stand-alone core.SubTaskContext,
diff --git a/plugins/helper/graphql_async_client.go b/plugins/helper/graphql_async_client.go
index 1dbb4484..b51b8a2f 100644
--- a/plugins/helper/graphql_async_client.go
+++ b/plugins/helper/graphql_async_client.go
@@ -19,7 +19,7 @@ package helper
 
 import (
 	"context"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/merico-dev/graphql"
 	"sync"
@@ -114,7 +114,7 @@ func (apiClient *GraphqlAsyncClient) Query(q interface{}, variables map[string]i
 	default:
 		err := apiClient.client.Query(apiClient.ctx, q, variables)
 		if err != nil {
-			return err
+			return errors.Default.Wrap(err, "error making GraphQL call", errors.UserMessage("Internal async GraphQL call error"))
 		}
 		cost := 1
 		if apiClient.getRateCost != nil {
@@ -147,7 +147,7 @@ func (apiClient *GraphqlAsyncClient) NextTick(task func() error) {
 func (apiClient *GraphqlAsyncClient) Wait() error {
 	apiClient.waitGroup.Wait()
 	if len(apiClient.workerErrors) > 0 {
-		return fmt.Errorf("%s", apiClient.workerErrors)
+		return errors.Default.Combine(apiClient.workerErrors, "graphql workers encountered error(s)")
 	}
 	return nil
 }
diff --git a/plugins/helper/graphql_collector.go b/plugins/helper/graphql_collector.go
index ed69e0cc..990aad7a 100644
--- a/plugins/helper/graphql_collector.go
+++ b/plugins/helper/graphql_collector.go
@@ -19,7 +19,7 @@ package helper
 
 import (
 	"encoding/json"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/models/common"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
@@ -82,16 +82,16 @@ func NewGraphqlCollector(args GraphqlCollectorArgs) (*GraphqlCollector, error) {
 	// process args
 	rawDataSubTask, err := newRawDataSubTask(args.RawDataSubTaskArgs)
 	if err != nil {
-		return nil, err
+		return nil, errors.Default.Wrap(err, "error processing raw subtask args", errors.UserMessage("Internal GraphQL Collector error"))
 	}
 	if err != nil {
-		return nil, fmt.Errorf("Failed to compile UrlTemplate: %w", err)
+		return nil, errors.Default.Wrap(err, "Failed to compile UrlTemplate", errors.UserMessage("Internal GraphQL Collector error"))
 	}
 	if args.GraphqlClient == nil {
-		return nil, fmt.Errorf("ApiClient is required")
+		return nil, errors.Default.New("ApiClient is required", errors.UserMessage("Internal GraphQL Collector error"))
 	}
 	if args.ResponseParser == nil {
-		return nil, fmt.Errorf("ResponseParser is required")
+		return nil, errors.Default.New("ResponseParser is required", errors.UserMessage("Internal GraphQL Collector error"))
 	}
 	apicllector := &GraphqlCollector{
 		RawDataSubTask: rawDataSubTask,
@@ -108,7 +108,7 @@ func NewGraphqlCollector(args GraphqlCollectorArgs) (*GraphqlCollector, error) {
 	//} else {
 	//	apicllector.SetAfterResponse(func(res *http.Response) error {
 	//		if res.StatusCode == http.StatusUnauthorized {
-	//			return fmt.Errorf("authentication failed, please check your AccessToken")
+	//			return errors.Default.New("authentication failed, please check your AccessToken")
 	//		}
 	//		return nil
 	//	})
@@ -125,13 +125,13 @@ func (collector *GraphqlCollector) Execute() error {
 	db := collector.args.Ctx.GetDal()
 	err := db.AutoMigrate(&RawData{}, dal.From(collector.table))
 	if err != nil {
-		return err
+		return errors.Default.Wrap(err, "error running auto-migrate", errors.UserMessage("Internal GraphQL Collector execution error"))
 	}
 
 	// flush data if not incremental collection
 	err = db.Delete(&RawData{}, dal.From(collector.table), dal.Where("params = ?", collector.params))
 	if err != nil {
-		return err
+		return errors.Default.Wrap(err, "error deleting from collector table", errors.UserMessage("Internal GraphQL Collector execution error"))
 	}
 	divider := NewBatchSaveDivider(collector.args.Ctx, collector.args.BatchSize, collector.table, collector.params)
 
@@ -168,9 +168,11 @@ func (collector *GraphqlCollector) Execute() error {
 
 	err = collector.args.GraphqlClient.Wait()
 	if err != nil {
-		logger.Info("end api collection error: %w", err)
+		err = errors.Default.Wrap(err, "ended API collector execution with error", errors.UserMessage("Internal GraphQL Collector execution error"))
+		logger.Error(err, "")
+
 	} else {
-		logger.Info("end api collection without error")
+		logger.Info("ended api collection without error")
 	}
 	err = divider.Close()
 
@@ -203,7 +205,7 @@ func (collector *GraphqlCollector) fetchOneByOne(divider *BatchSaveDivider, reqD
 	fetchNextPage = func(query interface{}) error {
 		pageInfo, err := collector.args.GetPageInfo(query, collector.args)
 		if err != nil {
-			return fmt.Errorf("fetchPagesDetermined get totalPages faileds: %s", err.Error())
+			return errors.Default.Wrap(err, "fetchPagesDetermined get totalPages failed")
 		}
 		if pageInfo.HasNextPage {
 			collector.args.GraphqlClient.NextTick(func() error {
@@ -240,7 +242,7 @@ func (collector *GraphqlCollector) fetchAsync(divider *BatchSaveDivider, reqData
 	err = collector.args.GraphqlClient.Query(query, variables)
 	if err != nil {
 		if collector.args.IgnoreQueryErr {
-			logger.Error("fetchAsync fail and got:", err)
+			logger.Error(err, "fetchAsync failed")
 			return
 		} else {
 			panic(err)
diff --git a/plugins/helper/pipeline_plan.go b/plugins/helper/pipeline_plan.go
index ed51f2fa..fd2a93a6 100644
--- a/plugins/helper/pipeline_plan.go
+++ b/plugins/helper/pipeline_plan.go
@@ -19,6 +19,7 @@ package helper
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/utils"
@@ -33,7 +34,7 @@ func MakePipelinePlanSubtasks(subtaskMetas []core.SubTaskMeta, entities []string
 	wanted := make(map[string]bool, len(entities))
 	for _, entity := range entities {
 		if !utils.StringsContains(core.DOMAIN_TYPES, entity) {
-			return nil, fmt.Errorf("invalid entity(domain type): %s", entity)
+			return nil, errors.Default.New(fmt.Sprintf("invalid entity(domain type): %s", entity))
 		}
 		wanted[entity] = true
 	}
diff --git a/plugins/helper/worker_scheduler.go b/plugins/helper/worker_scheduler.go
index b431cdf9..d5c59744 100644
--- a/plugins/helper/worker_scheduler.go
+++ b/plugins/helper/worker_scheduler.go
@@ -19,13 +19,13 @@ package helper
 
 import (
 	"context"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"sync"
 	"sync/atomic"
 	"time"
 
 	"github.com/apache/incubator-devlake/plugins/core"
-	ants "github.com/panjf2000/ants/v2"
+	"github.com/panjf2000/ants/v2"
 )
 
 // WorkerScheduler runs asynchronous tasks in parallel with throttling support
@@ -52,10 +52,10 @@ func NewWorkerScheduler(
 	logger core.Logger,
 ) (*WorkerScheduler, error) {
 	if maxWork <= 0 {
-		return nil, fmt.Errorf("maxWork less than 1")
+		return nil, errors.Default.New("maxWork less than 1")
 	}
 	if maxWorkDuration <= 0 {
-		return nil, fmt.Errorf("maxWorkDuration less than 1")
+		return nil, errors.Default.New("maxWorkDuration less than 1")
 	}
 	s := &WorkerScheduler{
 		ctx:    ctx,
@@ -152,7 +152,7 @@ func (s *WorkerScheduler) NextTick(task func() error) {
 func (s *WorkerScheduler) Wait() error {
 	s.waitGroup.Wait()
 	if len(s.workerErrors) > 0 {
-		return fmt.Errorf("%s", s.workerErrors)
+		return errors.Default.Combine(s.workerErrors, "worker scheduler captured these errors")
 	}
 	return nil
 }
diff --git a/plugins/icla/plugin_main.go b/plugins/icla/plugin_main.go
index 5bfcaa76..c1472ae0 100644
--- a/plugins/icla/plugin_main.go
+++ b/plugins/icla/plugin_main.go
@@ -19,6 +19,7 @@ package main
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/icla/models"
@@ -98,7 +99,7 @@ func (plugin Icla) ApiResources() map[string]map[string]core.ApiResourceHandler
 func (plugin Icla) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.IclaTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/jenkins/api/blueprint.go b/plugins/jenkins/api/blueprint.go
index ba06af35..c3ec10dc 100644
--- a/plugins/jenkins/api/blueprint.go
+++ b/plugins/jenkins/api/blueprint.go
@@ -19,7 +19,6 @@ package api
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jenkins/tasks"
diff --git a/plugins/jenkins/api/connection.go b/plugins/jenkins/api/connection.go
index be180c2e..abfd6754 100644
--- a/plugins/jenkins/api/connection.go
+++ b/plugins/jenkins/api/connection.go
@@ -20,6 +20,7 @@ package api
 import (
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"time"
 
@@ -74,7 +75,7 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	}
 
 	if res.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+		return nil, errors.HttpStatus(res.StatusCode).New("unexpected status code when testing connection")
 	}
 	return nil, nil
 }
diff --git a/plugins/jenkins/impl/impl.go b/plugins/jenkins/impl/impl.go
index 04503a08..6413a57b 100644
--- a/plugins/jenkins/impl/impl.go
+++ b/plugins/jenkins/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 
 	"github.com/apache/incubator-devlake/migration"
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -86,7 +87,7 @@ func (plugin Jenkins) PrepareTaskData(taskCtx core.TaskContext, options map[stri
 		return nil, err
 	}
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.BadInput.New("connectionId is invalid", errors.AsUserMessage())
 	}
 
 	connection := &models.JenkinsConnection{}
@@ -145,7 +146,7 @@ func (plugin Jenkins) ApiResources() map[string]map[string]core.ApiResourceHandl
 func (plugin Jenkins) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.JenkinsTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/jenkins/tasks/job_collector.go b/plugins/jenkins/tasks/job_collector.go
index dd5b7c2e..90a63fa7 100644
--- a/plugins/jenkins/tasks/job_collector.go
+++ b/plugins/jenkins/tasks/job_collector.go
@@ -20,12 +20,13 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
-	models "github.com/apache/incubator-devlake/plugins/jenkins/models"
+	"github.com/apache/incubator-devlake/plugins/jenkins/models"
 )
 
 const RAW_JOB_TABLE = "jenkins_api_jobs"
@@ -83,7 +84,7 @@ func CollectApiJobs(taskCtx core.SubTaskContext) error {
 					},
 				}, nil
 			} else {
-				return nil, fmt.Errorf("empty FolderInput")
+				return nil, errors.Default.New("empty FolderInput")
 			}
 		},
 
diff --git a/plugins/jenkins/tasks/task_data.go b/plugins/jenkins/tasks/task_data.go
index b0d35c6f..ae91a798 100644
--- a/plugins/jenkins/tasks/task_data.go
+++ b/plugins/jenkins/tasks/task_data.go
@@ -18,7 +18,7 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/plugins/helper"
@@ -47,11 +47,11 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*JenkinsOptio
 	var op JenkinsOptions
 	err := mapstructure.Decode(options, &op)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	// find the needed Jenkins now
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.BadInput.New("connectionId is invalid")
 	}
 	return &op, nil
 }
diff --git a/plugins/jira/api/blueprint.go b/plugins/jira/api/blueprint.go
index cbf5043b..28f7fbcb 100644
--- a/plugins/jira/api/blueprint.go
+++ b/plugins/jira/api/blueprint.go
@@ -19,7 +19,6 @@ package api
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jira/tasks"
diff --git a/plugins/jira/api/connection.go b/plugins/jira/api/connection.go
index 1fd5d666..ae32750a 100644
--- a/plugins/jira/api/connection.go
+++ b/plugins/jira/api/connection.go
@@ -20,12 +20,12 @@ package api
 import (
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"net/url"
 	"strings"
 	"time"
 
-	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jira/models"
@@ -46,12 +46,12 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	var connection models.TestConnectionRequest
 	err = mapstructure.Decode(input.Body, &connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	// validate
 	err = vld.Struct(connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not validate request parameters", errors.AsUserMessage())
 	}
 	// test connection
 	apiClient, err := helper.NewApiClient(
@@ -72,7 +72,7 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	if err != nil {
 		return nil, err
 	}
-	serverInfoFail := "You are failed on test the serverInfo: [ " + res.Request.URL.String() + " ]"
+	serverInfoFail := "Failed testing the serverInfo: [ " + res.Request.URL.String() + " ]"
 	// check if `/rest/` was missing
 	if res.StatusCode == http.StatusNotFound && !strings.HasSuffix(connection.Endpoint, "/rest/") {
 		endpointUrl, err := url.Parse(connection.Endpoint)
@@ -84,7 +84,7 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 			return nil, err
 		}
 		restUrl := endpointUrl.ResolveReference(refUrl)
-		return nil, errors.NewNotFound(fmt.Sprintf("Seems like an invalid Endpoint URL, please try %s", restUrl.String()))
+		return nil, errors.NotFound.New(fmt.Sprintf("Seems like an invalid Endpoint URL, please try %s", restUrl.String()), errors.AsUserMessage())
 	}
 	resBody := &models.JiraServerInfo{}
 	err = helper.UnmarshalResponse(res, resBody)
@@ -95,18 +95,18 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	if resBody.DeploymentType == models.DeploymentServer {
 		// only support 8.x.x or higher
 		if versions := resBody.VersionNumbers; len(versions) == 3 && versions[0] < 7 {
-			return nil, fmt.Errorf("%s Support JIRA Server 8+ only", serverInfoFail)
+			return nil, errors.Default.New(fmt.Sprintf("%s Support JIRA Server 8+ only", serverInfoFail))
 		}
 	}
 	if res.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("%s unexpected status code: %d", serverInfoFail, res.StatusCode)
+		return nil, errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("%s unexpected status code: %d", serverInfoFail, res.StatusCode))
 	}
 
 	// verify credential
 	getStatusFail := "an error occurred while making request to `/rest/api/2/status`"
 	res, err = apiClient.Get("api/2/status", nil, nil)
 	if err != nil {
-		return nil, fmt.Errorf("%s %s", getStatusFail, err)
+		return nil, errors.Default.Wrap(err, getStatusFail)
 	}
 	getStatusFail += ": [ " + res.Request.URL.String() + " ]"
 
@@ -115,18 +115,18 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 		resErrBody := &models.JiraErrorInfo{}
 		err = helper.UnmarshalResponse(res, resErrBody)
 		if err != nil {
-			return nil, fmt.Errorf("%s Unexpected status code: %d,and UnmarshalResponse error %s", getStatusFail, res.StatusCode, err)
+			return nil, errors.HttpStatus(res.StatusCode).Wrap(err, getStatusFail)
 		}
 		for _, em := range resErrBody.ErrorMessages {
 			if em == "error.no-permission" {
-				return nil, fmt.Errorf("%s We get the error %s ,it might you use the right token(password) but with the wrong username.please check your password", getStatusFail, em)
+				return nil, errors.Default.New(fmt.Sprintf("%s We get the error %s ,it might you use the right token(password) but with the wrong username.please check your password", getStatusFail, em))
 			}
 			errMsg += em + " \r\n"
 		}
 	}
 
 	if res.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("%s Unexpected [%s] status code: %d %s", getStatusFail, res.Request.URL, res.StatusCode, errMsg)
+		return nil, errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("%s Unexpected [%s] status code: %d %s", getStatusFail, res.Request.URL, res.StatusCode, errMsg))
 	}
 
 	return nil, nil
diff --git a/plugins/jira/impl/impl.go b/plugins/jira/impl/impl.go
index 2c38afc9..9c487c02 100644
--- a/plugins/jira/impl/impl.go
+++ b/plugins/jira/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"time"
 
@@ -136,10 +137,10 @@ func (plugin Jira) PrepareTaskData(taskCtx core.TaskContext, options map[string]
 	logger.Debug("%v", options)
 	err = mapstructure.Decode(options, &op)
 	if err != nil {
-		return nil, fmt.Errorf("could not decode Jira options: %v", err)
+		return nil, errors.Default.Wrap(err, "could not decode Jira options", errors.AsUserMessage())
 	}
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("jira connectionId is invalid")
+		return nil, errors.BadInput.New("jira connectionId is invalid", errors.AsUserMessage())
 	}
 	connection := &models.JiraConnection{}
 	connectionHelper := helper.NewConnectionHelper(
@@ -147,27 +148,27 @@ func (plugin Jira) PrepareTaskData(taskCtx core.TaskContext, options map[string]
 		nil,
 	)
 	if err != nil {
-		return nil, fmt.Errorf("could not get connection API instance for Jira: %v", err)
+		return nil, errors.BadInput.Wrap(err, "could not get connection API instance for Jira", errors.AsUserMessage())
 	}
 	err = connectionHelper.FirstById(connection, op.ConnectionId)
 	if err != nil {
-		return nil, fmt.Errorf("unable to get Jira connection: %v", err)
+		return nil, errors.Default.Wrap(err, "unable to get Jira connection", errors.AsUserMessage())
 	}
 
 	var since time.Time
 	if op.Since != "" {
 		since, err = time.Parse("2006-01-02T15:04:05Z", op.Since)
 		if err != nil {
-			return nil, fmt.Errorf("invalid value for `since`: %w", err)
+			return nil, errors.BadInput.Wrap(err, "invalid value for `since`", errors.AsUserMessage())
 		}
 	}
 	jiraApiClient, err := tasks.NewJiraApiClient(taskCtx, connection)
 	if err != nil {
-		return nil, fmt.Errorf("failed to create jira api client: %w", err)
+		return nil, errors.Default.Wrap(err, "failed to create jira api client", errors.AsUserMessage())
 	}
 	info, code, err := tasks.GetJiraServerInfo(jiraApiClient)
 	if err != nil || code != http.StatusOK || info == nil {
-		return nil, fmt.Errorf("fail to get server info: error:[%s] code:[%d]", err, code)
+		return nil, errors.HttpStatus(code).Wrap(err, "fail to get Jira server info", errors.AsUserMessage())
 	}
 	taskData := &tasks.JiraTaskData{
 		Options:        &op,
@@ -221,7 +222,7 @@ func (plugin Jira) ApiResources() map[string]map[string]core.ApiResourceHandler
 func (plugin Jira) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.JiraTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/jira/models/migrationscripts/20220407_add_source_table.go b/plugins/jira/models/migrationscripts/20220407_add_source_table.go
index d8c3eb29..7dceb0df 100644
--- a/plugins/jira/models/migrationscripts/20220407_add_source_table.go
+++ b/plugins/jira/models/migrationscripts/20220407_add_source_table.go
@@ -2,7 +2,6 @@ package migrationscripts
 
 import (
 	"context"
-
 	"github.com/apache/incubator-devlake/plugins/jira/models/migrationscripts/archived"
 	"gorm.io/gorm"
 )
diff --git a/plugins/jira/models/migrationscripts/20220505_rename_source_table.go b/plugins/jira/models/migrationscripts/20220505_rename_source_table.go
index ef201aa5..269d2377 100644
--- a/plugins/jira/models/migrationscripts/20220505_rename_source_table.go
+++ b/plugins/jira/models/migrationscripts/20220505_rename_source_table.go
@@ -17,7 +17,6 @@ package migrationscripts
 
 import (
 	"context"
-
 	"github.com/apache/incubator-devlake/models/common"
 	"github.com/apache/incubator-devlake/plugins/jira/models/migrationscripts/archived"
 	"gorm.io/gorm"
diff --git a/plugins/jira/models/migrationscripts/20220716_add_init_tables.go b/plugins/jira/models/migrationscripts/20220716_add_init_tables.go
index 8547d92d..edb629bf 100644
--- a/plugins/jira/models/migrationscripts/20220716_add_init_tables.go
+++ b/plugins/jira/models/migrationscripts/20220716_add_init_tables.go
@@ -20,7 +20,7 @@ package migrationscripts
 import (
 	"context"
 	"encoding/base64"
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"strings"
 	"time"
 
@@ -115,7 +115,7 @@ func (*addInitTables) Up(ctx context.Context, db *gorm.DB) error {
 			c := config.GetConfig()
 			encKey := c.GetString(core.EncodeKeyEnvStr)
 			if encKey == "" {
-				return fmt.Errorf("jira v0.11 invalid encKey")
+				return errors.BadInput.New("jira v0.11 invalid encKey", errors.AsUserMessage())
 			}
 			auth, err := core.Decrypt(encKey, v.BasicAuthEncoded)
 			if err != nil {
@@ -133,9 +133,9 @@ func (*addInitTables) Up(ctx context.Context, db *gorm.DB) error {
 					return err
 				}
 				// create
-				err := db.Create(&conn)
-				if err.Error != nil {
-					return err.Error
+				tx := db.Create(&conn)
+				if tx.Error != nil {
+					return errors.Default.Wrap(tx.Error, "error adding connection to DB")
 				}
 			}
 		}
diff --git a/plugins/jira/tasks/account_collector.go b/plugins/jira/tasks/account_collector.go
index cf0036da..d7c26c91 100644
--- a/plugins/jira/tasks/account_collector.go
+++ b/plugins/jira/tasks/account_collector.go
@@ -101,7 +101,7 @@ func CollectAccounts(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect account error:", err)
+		logger.Error(err, "collect account error")
 		return err
 	}
 
diff --git a/plugins/jira/tasks/account_extractor.go b/plugins/jira/tasks/account_extractor.go
index 8da8e784..bb0520e2 100644
--- a/plugins/jira/tasks/account_extractor.go
+++ b/plugins/jira/tasks/account_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jira/tasks/apiv2models"
diff --git a/plugins/jira/tasks/api_client.go b/plugins/jira/tasks/api_client.go
index 85312afc..150b4aca 100644
--- a/plugins/jira/tasks/api_client.go
+++ b/plugins/jira/tasks/api_client.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -65,7 +66,7 @@ func GetJiraServerInfo(client *helper.ApiAsyncClient) (*models.JiraServerInfo, i
 		return nil, 0, err
 	}
 	if res.StatusCode >= 300 || res.StatusCode < 200 {
-		return nil, res.StatusCode, fmt.Errorf("request failed with status code: %d", res.StatusCode)
+		return nil, res.StatusCode, errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("request failed with status code: %d", res.StatusCode))
 	}
 	serverInfo := &models.JiraServerInfo{}
 	err = helper.UnmarshalResponse(res, serverInfo)
@@ -77,7 +78,7 @@ func GetJiraServerInfo(client *helper.ApiAsyncClient) (*models.JiraServerInfo, i
 
 func ignoreHTTPStatus404(res *http.Response) error {
 	if res.StatusCode == http.StatusUnauthorized {
-		return fmt.Errorf("authentication failed, please check your AccessToken")
+		return errors.Unauthorized.New("authentication failed, please check your AccessToken")
 	}
 	if res.StatusCode == http.StatusNotFound {
 		return helper.ErrIgnoreAndContinue
diff --git a/plugins/jira/tasks/board_collector.go b/plugins/jira/tasks/board_collector.go
index f5b438bb..8b6646d8 100644
--- a/plugins/jira/tasks/board_collector.go
+++ b/plugins/jira/tasks/board_collector.go
@@ -65,7 +65,7 @@ func CollectBoard(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect board error:", err)
+		logger.Error(err, "collect board error")
 		return err
 	}
 
diff --git a/plugins/jira/tasks/board_extractor.go b/plugins/jira/tasks/board_extractor.go
index e0b370df..426d1399 100644
--- a/plugins/jira/tasks/board_extractor.go
+++ b/plugins/jira/tasks/board_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jira/tasks/apiv2models"
diff --git a/plugins/jira/tasks/epic_collector.go b/plugins/jira/tasks/epic_collector.go
index 082b0398..e24a957d 100644
--- a/plugins/jira/tasks/epic_collector.go
+++ b/plugins/jira/tasks/epic_collector.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"reflect"
@@ -125,7 +126,7 @@ func GetEpicKeysIterator(db dal.Dal, data *JiraTaskData, batchSize int) (helper.
 				i.epic_key != ''
 		`, data.Options.ConnectionId, data.Options.BoardId)
 	if err != nil {
-		return nil, fmt.Errorf("unable to query for external epics: %v", err)
+		return nil, errors.Default.Wrap(err, "unable to query for external epics")
 	}
 	iter, err := helper.NewBatchedDalCursorIterator(db, cursor, reflect.TypeOf(""), batchSize)
 	if err != nil {
diff --git a/plugins/jira/tasks/issue_changelog_convertor.go b/plugins/jira/tasks/issue_changelog_convertor.go
index 59559836..5041f872 100644
--- a/plugins/jira/tasks/issue_changelog_convertor.go
+++ b/plugins/jira/tasks/issue_changelog_convertor.go
@@ -81,7 +81,7 @@ func ConvertIssueChangelogs(taskCtx core.SubTaskContext) error {
 	}
 	cursor, err := db.Cursor(clauses...)
 	if err != nil {
-		logger.Error(err.Error())
+		logger.Error(err, "")
 		return err
 	}
 	defer cursor.Close()
diff --git a/plugins/jira/tasks/issue_changelog_extractor.go b/plugins/jira/tasks/issue_changelog_extractor.go
index 0499cf63..fab4db80 100644
--- a/plugins/jira/tasks/issue_changelog_extractor.go
+++ b/plugins/jira/tasks/issue_changelog_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jira/models"
diff --git a/plugins/jira/tasks/issue_collector.go b/plugins/jira/tasks/issue_collector.go
index 62e79524..85958642 100644
--- a/plugins/jira/tasks/issue_collector.go
+++ b/plugins/jira/tasks/issue_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -67,7 +68,7 @@ func CollectIssues(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest jira issue record: %w", err)
+			return errors.NotFound.Wrap(err, "failed to get latest jira issue record: %w")
 		}
 		if latestUpdated.IssueId > 0 {
 			since = &latestUpdated.Updated
diff --git a/plugins/jira/tasks/issue_repo_commit_convertor.go b/plugins/jira/tasks/issue_repo_commit_convertor.go
index b6470107..0eff75a1 100644
--- a/plugins/jira/tasks/issue_repo_commit_convertor.go
+++ b/plugins/jira/tasks/issue_repo_commit_convertor.go
@@ -18,12 +18,10 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
+	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"reflect"
 	"regexp"
-	"runtime/debug"
-
-	"github.com/apache/incubator-devlake/plugins/core/dal"
 
 	"github.com/apache/incubator-devlake/models/domainlayer/crossdomain"
 	"github.com/apache/incubator-devlake/models/domainlayer/didgen"
@@ -54,7 +52,7 @@ func ConvertIssueRepoCommits(taskCtx core.SubTaskContext) error {
 	commitRepoUrlPattern := `(.*)\-\/commit`
 	commitRepoUrlRegex, err = regexp.Compile(commitRepoUrlPattern)
 	if err != nil {
-		return fmt.Errorf("regexp Compile commitRepoUrlPattern failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+		return errors.Default.Wrap(err, "regexp Compile commitRepoUrlPattern failed")
 	}
 
 	clause := []dal.Clause{
diff --git a/plugins/jira/tasks/issue_type_extractor.go b/plugins/jira/tasks/issue_type_extractor.go
index bbb41b02..616a660b 100644
--- a/plugins/jira/tasks/issue_type_extractor.go
+++ b/plugins/jira/tasks/issue_type_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jira/models"
diff --git a/plugins/jira/tasks/project_collector.go b/plugins/jira/tasks/project_collector.go
index 94f33ffb..677821c5 100644
--- a/plugins/jira/tasks/project_collector.go
+++ b/plugins/jira/tasks/project_collector.go
@@ -67,7 +67,7 @@ func CollectProjects(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect project error:", err)
+		logger.Error(err, "collect project error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/jira/tasks/project_extractor.go b/plugins/jira/tasks/project_extractor.go
index fc5a0461..00023408 100644
--- a/plugins/jira/tasks/project_extractor.go
+++ b/plugins/jira/tasks/project_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jira/tasks/apiv2models"
diff --git a/plugins/jira/tasks/remotelink_collector.go b/plugins/jira/tasks/remotelink_collector.go
index 529a0949..412f4216 100644
--- a/plugins/jira/tasks/remotelink_collector.go
+++ b/plugins/jira/tasks/remotelink_collector.go
@@ -62,7 +62,7 @@ func CollectRemotelinks(taskCtx core.SubTaskContext) error {
 	}
 	cursor, err := db.Cursor(clauses...)
 	if err != nil {
-		logger.Error("collect remotelink error:%v", err)
+		logger.Error(err, "collect remotelink error")
 		return err
 	}
 
diff --git a/plugins/jira/tasks/remotelink_extractor.go b/plugins/jira/tasks/remotelink_extractor.go
index 4b5c1206..91433b1d 100644
--- a/plugins/jira/tasks/remotelink_extractor.go
+++ b/plugins/jira/tasks/remotelink_extractor.go
@@ -19,15 +19,13 @@ package tasks
 
 import (
 	"encoding/json"
-	"fmt"
-	"regexp"
-	"runtime/debug"
-
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jira/models"
 	"github.com/apache/incubator-devlake/plugins/jira/tasks/apiv2models"
+	"regexp"
 )
 
 var ExtractRemotelinksMeta = core.SubTaskMeta{
@@ -50,7 +48,7 @@ func ExtractRemotelinks(taskCtx core.SubTaskContext) error {
 	if pattern := data.Options.TransformationRules.RemotelinkCommitShaPattern; pattern != "" {
 		commitShaRegex, err = regexp.Compile(pattern)
 		if err != nil {
-			return fmt.Errorf("regexp Compile pattern failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile pattern failed")
 		}
 	}
 
diff --git a/plugins/jira/tasks/status_extractor.go b/plugins/jira/tasks/status_extractor.go
index 56270a5b..efd3044f 100644
--- a/plugins/jira/tasks/status_extractor.go
+++ b/plugins/jira/tasks/status_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/jira/models"
diff --git a/plugins/jira/tasks/task_data.go b/plugins/jira/tasks/task_data.go
index 039e4080..958a15be 100644
--- a/plugins/jira/tasks/task_data.go
+++ b/plugins/jira/tasks/task_data.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/plugins/helper"
@@ -61,10 +62,10 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*JiraOptions,
 		return nil, err
 	}
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("invalid connectionId:%d", op.ConnectionId)
+		return nil, errors.BadInput.New(fmt.Sprintf("invalid connectionId:%d", op.ConnectionId), errors.AsUserMessage())
 	}
 	if op.BoardId == 0 {
-		return nil, fmt.Errorf("invalid boardId:%d", op.BoardId)
+		return nil, errors.BadInput.New(fmt.Sprintf("invalid boardId:%d", op.BoardId), errors.AsUserMessage())
 	}
 	return &op, nil
 }
diff --git a/plugins/jira/tasks/worklog_collector.go b/plugins/jira/tasks/worklog_collector.go
index 9d4694a8..864188a6 100644
--- a/plugins/jira/tasks/worklog_collector.go
+++ b/plugins/jira/tasks/worklog_collector.go
@@ -98,7 +98,7 @@ func CollectWorklogs(taskCtx core.SubTaskContext) error {
 		AfterResponse: ignoreHTTPStatus404,
 	})
 	if err != nil {
-		logger.Error("collect board error:", err)
+		logger.Error(err, "collect board error")
 		return err
 	}
 
diff --git a/plugins/jira/tasks/worklog_convertor.go b/plugins/jira/tasks/worklog_convertor.go
index 628e99b4..d1b9af72 100644
--- a/plugins/jira/tasks/worklog_convertor.go
+++ b/plugins/jira/tasks/worklog_convertor.go
@@ -54,7 +54,7 @@ func ConvertWorklogs(taskCtx core.SubTaskContext) error {
 	}
 	cursor, err := db.Cursor(clauses...)
 	if err != nil {
-		logger.Error("convert worklog error:", err)
+		logger.Error(err, "convert worklog error")
 		return err
 	}
 	defer cursor.Close()
diff --git a/plugins/org/api/handlers.go b/plugins/org/api/handlers.go
index ee5947e6..bcfa344c 100644
--- a/plugins/org/api/handlers.go
+++ b/plugins/org/api/handlers.go
@@ -19,7 +19,7 @@ package api
 
 import (
 	"encoding/csv"
-	"errors"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -39,7 +39,7 @@ func NewHandlers(db dal.Dal, basicRes core.BasicRes) *Handlers {
 
 func (h *Handlers) unmarshal(r *http.Request, items interface{}) error {
 	if r == nil {
-		return errors.New("request is nil")
+		return errors.Default.New("request is nil")
 	}
 	if r.MultipartForm == nil {
 		if err := r.ParseMultipartForm(maxMemory); err != nil {
diff --git a/plugins/org/impl/impl.go b/plugins/org/impl/impl.go
index daa27223..f2a15e3f 100644
--- a/plugins/org/impl/impl.go
+++ b/plugins/org/impl/impl.go
@@ -18,6 +18,7 @@ limitations under the License.
 package impl
 
 import (
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/impl/dalgorm"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
@@ -60,7 +61,7 @@ func (plugin Org) PrepareTaskData(taskCtx core.TaskContext, options map[string]i
 	var op tasks.Options
 	err := mapstructure.Decode(options, &op)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode options", errors.AsUserMessage())
 	}
 	taskData := &tasks.TaskData{
 		Options: &op,
diff --git a/plugins/refdiff/tasks/ref_commit_diff_calculator.go b/plugins/refdiff/tasks/ref_commit_diff_calculator.go
index bfea89a5..c8be7291 100644
--- a/plugins/refdiff/tasks/ref_commit_diff_calculator.go
+++ b/plugins/refdiff/tasks/ref_commit_diff_calculator.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"reflect"
 
 	"github.com/apache/incubator-devlake/models/domainlayer/code"
@@ -60,7 +61,7 @@ func CalculateCommitsDiff(taskCtx core.SubTaskContext) error {
 		}
 		err = db.Fetch(cursor, commitParent)
 		if err != nil {
-			return fmt.Errorf("failed to read commit from database: %v", err)
+			return errors.Default.Wrap(err, "failed to read commit from database")
 		}
 		commitNodeGraph.AddParent(commitParent.CommitSha, commitParent.ParentCommitSha)
 	}
diff --git a/plugins/refdiff/tasks/refdiff_task_data.go b/plugins/refdiff/tasks/refdiff_task_data.go
index 8a393fc3..4cf55f49 100644
--- a/plugins/refdiff/tasks/refdiff_task_data.go
+++ b/plugins/refdiff/tasks/refdiff_task_data.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"regexp"
 	"sort"
@@ -155,7 +156,7 @@ func CaculateTagPattern(db dal.Dal, tagsPattern string, tagsLimit int, tagsOrder
 	defer rows.Next()
 	r, err := regexp.Compile(tagsPattern)
 	if err != nil {
-		return rs, fmt.Errorf("unable to parse: %s\r\n%s", tagsPattern, err.Error())
+		return rs, errors.Default.Wrap(err, fmt.Sprintf("unable to parse: %s", tagsPattern))
 	}
 	for rows.Next() {
 		var ref code.Ref
@@ -199,12 +200,12 @@ func CalculateCommitPairs(db dal.Dal, repoId string, pairs []RefPair, rs Refs) (
 	ref2sha := func(refName string) (string, error) {
 		ref := &code.Ref{}
 		if refName == "" {
-			return "", fmt.Errorf("ref name is empty")
+			return "", errors.Default.New("ref name is empty")
 		}
 		ref.Id = fmt.Sprintf("%s:%s", repoId, refName)
 		err := db.First(ref)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return "", fmt.Errorf("faild to load Ref info for repoId:%s, refName:%s", repoId, refName)
+			return "", errors.NotFound.Wrap(err, fmt.Sprintf("faild to load Ref info for repoId:%s, refName:%s", repoId, refName))
 		}
 		return ref.CommitSha, nil
 	}
@@ -213,12 +214,12 @@ func CalculateCommitPairs(db dal.Dal, repoId string, pairs []RefPair, rs Refs) (
 		// get new ref's commit sha
 		newCommit, err := ref2sha(refPair.NewRef)
 		if err != nil {
-			return RefCommitPairs{}, fmt.Errorf("failed to load commit sha for NewRef on pair #%d: %w", i, err)
+			return RefCommitPairs{}, errors.Default.Wrap(err, fmt.Sprintf("failed to load commit sha for NewRef on pair #%d", i))
 		}
 		// get old ref's commit sha
 		oldCommit, err := ref2sha(refPair.OldRef)
 		if err != nil {
-			return RefCommitPairs{}, fmt.Errorf("failed to load commit sha for OleRef on pair #%d: %w", i, err)
+			return RefCommitPairs{}, errors.Default.Wrap(err, fmt.Sprintf("failed to load commit sha for OleRef on pair #%d", i))
 		}
 
 		have := false
diff --git a/plugins/refdiff/tasks/refs_pr_cherry_pick_calculator.go b/plugins/refdiff/tasks/refs_pr_cherry_pick_calculator.go
index 3acbeb8c..e4c01810 100644
--- a/plugins/refdiff/tasks/refs_pr_cherry_pick_calculator.go
+++ b/plugins/refdiff/tasks/refs_pr_cherry_pick_calculator.go
@@ -18,9 +18,8 @@ limitations under the License.
 package tasks
 
 import (
-	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"regexp"
-	"runtime/debug"
 	"strconv"
 	"strings"
 	"time"
@@ -51,7 +50,7 @@ func CalculatePrCherryPick(taskCtx core.SubTaskContext) error {
 	if len(prTitlePattern) > 0 {
 		prTitleRegex, err = regexp.Compile(prTitlePattern)
 		if err != nil {
-			return fmt.Errorf("regexp Compile prTitlePattern failed:[%s] stack:[%s]", err.Error(), debug.Stack())
+			return errors.Default.Wrap(err, "regexp Compile prTitlePattern failed")
 		}
 	}
 
diff --git a/plugins/starrocks/tasks.go b/plugins/starrocks/tasks.go
index ece4131c..dba0ebd5 100644
--- a/plugins/starrocks/tasks.go
+++ b/plugins/starrocks/tasks.go
@@ -21,6 +21,7 @@ import (
 	"database/sql"
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/impl/dalgorm"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
@@ -69,7 +70,7 @@ func LoadData(c core.SubTaskContext) error {
 	if config.DomainLayer != "" {
 		starrocksTables = getTablesByDomainLayer(config.DomainLayer)
 		if starrocksTables == nil {
-			return fmt.Errorf("no table found by domain layer: %s", config.DomainLayer)
+			return errors.NotFound.New(fmt.Sprintf("no table found by domain layer: %s", config.DomainLayer))
 		}
 	} else {
 		tables := config.Tables
@@ -104,12 +105,12 @@ func LoadData(c core.SubTaskContext) error {
 		starrocksTable := strings.TrimLeft(table, "_")
 		err = createTable(starrocks, db, starrocksTable, table, c, config.Extra)
 		if err != nil {
-			c.GetLogger().Error("create table %s in starrocks error: %s", table, err)
+			c.GetLogger().Error(err, "create table %s in starrocks error", table)
 			return err
 		}
 		err = loadData(starrocks, c, starrocksTable, table, db, config)
 		if err != nil {
-			c.GetLogger().Error("load data %s error: %s", table, err)
+			c.GetLogger().Error(err, "load data %s error", table)
 			return err
 		}
 	}
@@ -127,7 +128,7 @@ func createTable(starrocks *sql.DB, db dal.Dal, starrocksTable string, table str
 		name := cm.Name()
 		starrocksDatatype, ok := cm.ColumnType()
 		if !ok {
-			return fmt.Errorf("Get [%s] ColumeType Failed", name)
+			return errors.Default.New(fmt.Sprintf("Get [%s] ColumeType Failed", name))
 		}
 		column := fmt.Sprintf("`%s` %s", name, getDataType(starrocksDatatype))
 		columns = append(columns, column)
@@ -189,7 +190,7 @@ func loadData(starrocks *sql.DB, c core.SubTaskContext, starrocksTable string, t
 			data = append(data, row)
 		}
 		if len(data) == 0 {
-			c.GetLogger().Warn("no data found in table %s already, limit: %d, offset: %d, so break", table, config.BatchSize, offset)
+			c.GetLogger().Warn(nil, "no data found in table %s already, limit: %d, offset: %d, so break", table, config.BatchSize, offset)
 			break
 		}
 		// insert data to tmp table
@@ -248,10 +249,10 @@ func loadData(starrocks *sql.DB, c core.SubTaskContext, starrocksTable string, t
 			return err
 		}
 		if resp.StatusCode != http.StatusOK {
-			c.GetLogger().Error("%s %s", resp.StatusCode, b)
+			c.GetLogger().Error(nil, "[%s]: %s", resp.StatusCode, string(b))
 		}
 		if result["Status"] != "Success" {
-			c.GetLogger().Error("load %s failed: %s", table, b)
+			c.GetLogger().Error(nil, "load %s failed: %s", table, string(b))
 		} else {
 			c.GetLogger().Info("load %s success: %s, limit: %d, offset: %d", table, b, config.BatchSize, offset)
 		}
diff --git a/plugins/tapd/api/connection.go b/plugins/tapd/api/connection.go
index d3538fa3..4abaa8da 100644
--- a/plugins/tapd/api/connection.go
+++ b/plugins/tapd/api/connection.go
@@ -20,6 +20,7 @@ package api
 import (
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 	"time"
 
@@ -44,11 +45,11 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 	var connection models.TestConnectionRequest
 	err := mapstructure.Decode(input.Body, &connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not decode request parameters", errors.AsUserMessage())
 	}
 	err = vld.Struct(connection)
 	if err != nil {
-		return nil, err
+		return nil, errors.BadInput.Wrap(err, "could not validate request parameters", errors.AsUserMessage())
 	}
 
 	// verify multiple token in parallel
@@ -64,17 +65,17 @@ func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, erro
 		basicRes,
 	)
 	if err != nil {
-		return nil, fmt.Errorf("verify token failed for %s %w", connection.Username, err)
+		return nil, errors.Default.Wrap(err, fmt.Sprintf("verify token failed for %s", connection.Username))
 	}
 	res, err := apiClient.Get("/quickstart/testauth", nil, nil)
 	if err != nil {
 		return nil, err
 	}
 	if res.StatusCode == http.StatusUnauthorized {
-		return nil, fmt.Errorf("verify token failed for %s", connection.Username)
+		return nil, errors.Unauthorized.New(fmt.Sprintf("verify token failed for %s", connection.Username))
 	}
 	if res.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+		return nil, errors.HttpStatus(res.StatusCode).New(fmt.Sprintf("unexpected status code: %d", res.StatusCode))
 	}
 	// output
 	return nil, nil
diff --git a/plugins/tapd/impl/impl.go b/plugins/tapd/impl/impl.go
index fc26f21d..ec8bcc76 100644
--- a/plugins/tapd/impl/impl.go
+++ b/plugins/tapd/impl/impl.go
@@ -19,6 +19,7 @@ package impl
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"time"
 
 	"github.com/apache/incubator-devlake/plugins/helper"
@@ -165,7 +166,7 @@ func (plugin Tapd) PrepareTaskData(taskCtx core.TaskContext, options map[string]
 		return nil, err
 	}
 	if op.ConnectionId == 0 {
-		return nil, fmt.Errorf("connectionId is invalid")
+		return nil, errors.BadInput.New("connectionId is invalid", errors.AsUserMessage())
 	}
 	connection := &models.TapdConnection{}
 	connectionHelper := helper.NewConnectionHelper(
@@ -181,7 +182,7 @@ func (plugin Tapd) PrepareTaskData(taskCtx core.TaskContext, options map[string]
 	if op.Since != "" {
 		since, err = time.Parse("2006-01-02T15:04:05Z", op.Since)
 		if err != nil {
-			return nil, fmt.Errorf("invalid value for `since`: %w", err)
+			return nil, errors.BadInput.Wrap(err, "invalid value for `since`", errors.AsUserMessage())
 		}
 	}
 	if connection.RateLimitPerHour == 0 {
@@ -189,7 +190,7 @@ func (plugin Tapd) PrepareTaskData(taskCtx core.TaskContext, options map[string]
 	}
 	tapdApiClient, err := tasks.NewTapdApiClient(taskCtx, connection)
 	if err != nil {
-		return nil, fmt.Errorf("failed to create tapd api client: %w", err)
+		return nil, errors.Default.Wrap(err, "failed to create tapd api client")
 	}
 	taskData := &tasks.TapdTaskData{
 		Options:    &op,
@@ -234,7 +235,7 @@ func (plugin Tapd) ApiResources() map[string]map[string]core.ApiResourceHandler
 func (plugin Tapd) Close(taskCtx core.TaskContext) error {
 	data, ok := taskCtx.GetData().(*tasks.TapdTaskData)
 	if !ok {
-		return fmt.Errorf("GetData failed when try to close %+v", taskCtx)
+		return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx))
 	}
 	data.ApiClient.Release()
 	return nil
diff --git a/plugins/tapd/models/connection.go b/plugins/tapd/models/connection.go
index c10f116b..963cdbdd 100644
--- a/plugins/tapd/models/connection.go
+++ b/plugins/tapd/models/connection.go
@@ -20,7 +20,6 @@ package models
 import (
 	"encoding/base64"
 	"fmt"
-
 	"github.com/apache/incubator-devlake/plugins/helper"
 )
 
diff --git a/plugins/tapd/models/task_changelog.go b/plugins/tapd/models/task_changelog.go
index 29df87e3..f8918392 100644
--- a/plugins/tapd/models/task_changelog.go
+++ b/plugins/tapd/models/task_changelog.go
@@ -19,7 +19,6 @@ package models
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/models/common"
 	"github.com/apache/incubator-devlake/plugins/helper"
 )
diff --git a/plugins/tapd/tasks/account_collector.go b/plugins/tapd/tasks/account_collector.go
index 547d1902..2eaf06c8 100644
--- a/plugins/tapd/tasks/account_collector.go
+++ b/plugins/tapd/tasks/account_collector.go
@@ -64,7 +64,7 @@ func CollectAccounts(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect user error:", err)
+		logger.Error(err, "collect user error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/account_extractor.go b/plugins/tapd/tasks/account_extractor.go
index 05eb4971..0feb3bb0 100644
--- a/plugins/tapd/tasks/account_extractor.go
+++ b/plugins/tapd/tasks/account_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/api_client.go b/plugins/tapd/tasks/api_client.go
index c5b0c480..fdf259b4 100644
--- a/plugins/tapd/tasks/api_client.go
+++ b/plugins/tapd/tasks/api_client.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/base64"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/http"
 
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -40,7 +41,7 @@ func NewTapdApiClient(taskCtx core.TaskContext, connection *models.TapdConnectio
 	}
 	apiClient.SetAfterFunction(func(res *http.Response) error {
 		if res.StatusCode == http.StatusUnprocessableEntity {
-			return fmt.Errorf("authentication failed, please check your AccessToken")
+			return errors.HttpStatus(res.StatusCode).New("authentication failed, please check your AccessToken")
 		}
 		return nil
 	})
diff --git a/plugins/tapd/tasks/bug_changelog_collector.go b/plugins/tapd/tasks/bug_changelog_collector.go
index ca7fb089..5a208962 100644
--- a/plugins/tapd/tasks/bug_changelog_collector.go
+++ b/plugins/tapd/tasks/bug_changelog_collector.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/url"
 	"time"
@@ -49,7 +50,7 @@ func CollectBugChangelogs(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.NotFound.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Created)
@@ -77,7 +78,7 @@ func CollectBugChangelogs(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageArrayFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect story changelog error:", err)
+		logger.Error(err, "collect story changelog error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/bug_changelog_extractor.go b/plugins/tapd/tasks/bug_changelog_extractor.go
index 0680a7f7..07ba2d74 100644
--- a/plugins/tapd/tasks/bug_changelog_extractor.go
+++ b/plugins/tapd/tasks/bug_changelog_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/bug_collector.go b/plugins/tapd/tasks/bug_collector.go
index 3ed97f11..573459e9 100644
--- a/plugins/tapd/tasks/bug_collector.go
+++ b/plugins/tapd/tasks/bug_collector.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/url"
 	"time"
@@ -50,7 +51,7 @@ func CollectBugs(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Modified)
@@ -79,7 +80,7 @@ func CollectBugs(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageArrayFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect bug error:", err)
+		logger.Error(err, "collect bug error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/bug_commit_collector.go b/plugins/tapd/tasks/bug_commit_collector.go
index 4cfdb33a..65f38355 100644
--- a/plugins/tapd/tasks/bug_commit_collector.go
+++ b/plugins/tapd/tasks/bug_commit_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/http"
 	"net/url"
@@ -57,7 +58,7 @@ func CollectBugCommits(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.NotFound.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Created)
@@ -111,7 +112,7 @@ func CollectBugCommits(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect issueCommit error:", err)
+		logger.Error(err, "collect issueCommit error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/bug_commit_extractor.go b/plugins/tapd/tasks/bug_commit_extractor.go
index c07ce791..6a55e3c4 100644
--- a/plugins/tapd/tasks/bug_commit_extractor.go
+++ b/plugins/tapd/tasks/bug_commit_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/bug_custom_fields_collector.go b/plugins/tapd/tasks/bug_custom_fields_collector.go
index 6cfda606..c1a14690 100644
--- a/plugins/tapd/tasks/bug_custom_fields_collector.go
+++ b/plugins/tapd/tasks/bug_custom_fields_collector.go
@@ -54,7 +54,7 @@ func CollectBugCustomFields(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect bug_custom_fields error:", err)
+		logger.Error(err, "collect bug_custom_fields error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/bug_custom_fields_extractor.go b/plugins/tapd/tasks/bug_custom_fields_extractor.go
index 26555e38..8bc239de 100644
--- a/plugins/tapd/tasks/bug_custom_fields_extractor.go
+++ b/plugins/tapd/tasks/bug_custom_fields_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/bug_status_collector.go b/plugins/tapd/tasks/bug_status_collector.go
index 745505cb..31de3e53 100644
--- a/plugins/tapd/tasks/bug_status_collector.go
+++ b/plugins/tapd/tasks/bug_status_collector.go
@@ -48,7 +48,7 @@ func CollectBugStatus(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageDirectFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect bugStatus error:", err)
+		logger.Error(err, "collect bugStatus error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/bug_status_extractor.go b/plugins/tapd/tasks/bug_status_extractor.go
index 8cd538fd..eb529f3b 100644
--- a/plugins/tapd/tasks/bug_status_extractor.go
+++ b/plugins/tapd/tasks/bug_status_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/company_collector.go b/plugins/tapd/tasks/company_collector.go
index 7117f7b5..2cc68ba0 100644
--- a/plugins/tapd/tasks/company_collector.go
+++ b/plugins/tapd/tasks/company_collector.go
@@ -56,7 +56,7 @@ func CollectCompanies(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect company error:", err)
+		logger.Error(err, "collect company error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/iteration_collector.go b/plugins/tapd/tasks/iteration_collector.go
index 8b5416cb..1e825307 100644
--- a/plugins/tapd/tasks/iteration_collector.go
+++ b/plugins/tapd/tasks/iteration_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/http"
 	"net/url"
@@ -51,7 +52,7 @@ func CollectIterations(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.NotFound.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Modified)
@@ -84,7 +85,7 @@ func CollectIterations(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect iteration error:", err)
+		logger.Error(err, "collect iteration error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/iteration_extractor.go b/plugins/tapd/tasks/iteration_extractor.go
index cc1d29e9..8cec9ff1 100644
--- a/plugins/tapd/tasks/iteration_extractor.go
+++ b/plugins/tapd/tasks/iteration_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/story_bug_collector.go b/plugins/tapd/tasks/story_bug_collector.go
index 8123a940..6195fd0c 100644
--- a/plugins/tapd/tasks/story_bug_collector.go
+++ b/plugins/tapd/tasks/story_bug_collector.go
@@ -66,7 +66,7 @@ func CollectStoryBugs(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageArrayFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect storyBug error:", err)
+		logger.Error(err, "collect storyBug error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/story_bug_extractor.go b/plugins/tapd/tasks/story_bug_extractor.go
index 89ac756e..6d67de7f 100644
--- a/plugins/tapd/tasks/story_bug_extractor.go
+++ b/plugins/tapd/tasks/story_bug_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/story_category_collector.go b/plugins/tapd/tasks/story_category_collector.go
index b4e1193c..3dec4c1e 100644
--- a/plugins/tapd/tasks/story_category_collector.go
+++ b/plugins/tapd/tasks/story_category_collector.go
@@ -54,7 +54,7 @@ func CollectStoryCategories(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect story_category error:", err)
+		logger.Error(err, "collect story_category error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/story_category_extractor.go b/plugins/tapd/tasks/story_category_extractor.go
index 72e82558..e35b51bb 100644
--- a/plugins/tapd/tasks/story_category_extractor.go
+++ b/plugins/tapd/tasks/story_category_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/story_changelog_collector.go b/plugins/tapd/tasks/story_changelog_collector.go
index c0bfc291..a040b117 100644
--- a/plugins/tapd/tasks/story_changelog_collector.go
+++ b/plugins/tapd/tasks/story_changelog_collector.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"github.com/apache/incubator-devlake/plugins/helper"
@@ -48,7 +49,7 @@ func CollectStoryChangelogs(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.NotFound.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Created)
@@ -76,7 +77,7 @@ func CollectStoryChangelogs(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageArrayFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect story changelog error:", err)
+		logger.Error(err, "collect story changelog error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/story_collector.go b/plugins/tapd/tasks/story_collector.go
index 7ae30af1..a6d8f02f 100644
--- a/plugins/tapd/tasks/story_collector.go
+++ b/plugins/tapd/tasks/story_collector.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/url"
 	"time"
@@ -49,7 +50,7 @@ func CollectStorys(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.Default.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Modified)
@@ -77,7 +78,7 @@ func CollectStorys(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageArrayFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect story error:", err)
+		logger.Error(err, "collect story error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/story_commit_collector.go b/plugins/tapd/tasks/story_commit_collector.go
index 80ba76cd..d1a2cd17 100644
--- a/plugins/tapd/tasks/story_commit_collector.go
+++ b/plugins/tapd/tasks/story_commit_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/http"
 	"net/url"
@@ -57,7 +58,7 @@ func CollectStoryCommits(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.NotFound.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Created)
@@ -111,7 +112,7 @@ func CollectStoryCommits(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect issueCommit error:", err)
+		logger.Error(err, "collect issueCommit error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/story_commit_extractor.go b/plugins/tapd/tasks/story_commit_extractor.go
index 4834ad0f..ddb6a33d 100644
--- a/plugins/tapd/tasks/story_commit_extractor.go
+++ b/plugins/tapd/tasks/story_commit_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/story_custom_fields_collector.go b/plugins/tapd/tasks/story_custom_fields_collector.go
index 1bb8efcd..8b9fa267 100644
--- a/plugins/tapd/tasks/story_custom_fields_collector.go
+++ b/plugins/tapd/tasks/story_custom_fields_collector.go
@@ -54,7 +54,7 @@ func CollectStoryCustomFields(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect story_custom_fields error:", err)
+		logger.Error(err, "collect story_custom_fields error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/story_custom_fields_extractor.go b/plugins/tapd/tasks/story_custom_fields_extractor.go
index a451c9f3..ea7a6327 100644
--- a/plugins/tapd/tasks/story_custom_fields_extractor.go
+++ b/plugins/tapd/tasks/story_custom_fields_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/story_status_collector.go b/plugins/tapd/tasks/story_status_collector.go
index 63d1d00d..c82bddf5 100644
--- a/plugins/tapd/tasks/story_status_collector.go
+++ b/plugins/tapd/tasks/story_status_collector.go
@@ -48,7 +48,7 @@ func CollectStoryStatus(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageDirectFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect bugStatus error:", err)
+		logger.Error(err, "collect bugStatus error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/story_status_extractor.go b/plugins/tapd/tasks/story_status_extractor.go
index 0e766154..208b3414 100644
--- a/plugins/tapd/tasks/story_status_extractor.go
+++ b/plugins/tapd/tasks/story_status_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/sub_workspace_collector.go b/plugins/tapd/tasks/sub_workspace_collector.go
index 542df6bb..a83fefca 100644
--- a/plugins/tapd/tasks/sub_workspace_collector.go
+++ b/plugins/tapd/tasks/sub_workspace_collector.go
@@ -56,7 +56,7 @@ func CollectSubWorkspaces(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect workspace error:", err)
+		logger.Error(err, "collect workspace error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/sub_workspace_extractor.go b/plugins/tapd/tasks/sub_workspace_extractor.go
index 86001ef5..bc0aab29 100644
--- a/plugins/tapd/tasks/sub_workspace_extractor.go
+++ b/plugins/tapd/tasks/sub_workspace_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/task_changelog_collector.go b/plugins/tapd/tasks/task_changelog_collector.go
index 68600ec1..66f79fa1 100644
--- a/plugins/tapd/tasks/task_changelog_collector.go
+++ b/plugins/tapd/tasks/task_changelog_collector.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/url"
 	"time"
@@ -49,7 +50,7 @@ func CollectTaskChangelogs(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.NotFound.Wrap(err, fmt.Sprintf("failed to get latest tapd changelog record"))
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Created)
@@ -77,7 +78,7 @@ func CollectTaskChangelogs(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageArrayFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect task changelog error:", err)
+		logger.Error(err, "collect task changelog error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/task_collector.go b/plugins/tapd/tasks/task_collector.go
index d63e6228..21c11d94 100644
--- a/plugins/tapd/tasks/task_collector.go
+++ b/plugins/tapd/tasks/task_collector.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/url"
 	"time"
@@ -51,7 +52,7 @@ func CollectTasks(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.NotFound.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Modified)
@@ -80,7 +81,7 @@ func CollectTasks(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageArrayFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect task error:", err)
+		logger.Error(err, "collect task error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/task_commit_collector.go b/plugins/tapd/tasks/task_commit_collector.go
index a8cfd1bf..217437af 100644
--- a/plugins/tapd/tasks/task_commit_collector.go
+++ b/plugins/tapd/tasks/task_commit_collector.go
@@ -20,6 +20,7 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/http"
 	"net/url"
@@ -57,7 +58,7 @@ func CollectTaskCommits(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.NotFound.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Created)
@@ -111,7 +112,7 @@ func CollectTaskCommits(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect issueCommit error:", err)
+		logger.Error(err, "collect issueCommit error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/task_commit_extractor.go b/plugins/tapd/tasks/task_commit_extractor.go
index 028afbf0..20566039 100644
--- a/plugins/tapd/tasks/task_commit_extractor.go
+++ b/plugins/tapd/tasks/task_commit_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/task_custom_fields_collector.go b/plugins/tapd/tasks/task_custom_fields_collector.go
index 4551a763..ba03b2f0 100644
--- a/plugins/tapd/tasks/task_custom_fields_collector.go
+++ b/plugins/tapd/tasks/task_custom_fields_collector.go
@@ -54,7 +54,7 @@ func CollectTaskCustomFields(taskCtx core.SubTaskContext) error {
 		},
 	})
 	if err != nil {
-		logger.Error("collect task_custom_fields error:", err)
+		logger.Error(err, "collect task_custom_fields error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/task_custom_fields_extractor.go b/plugins/tapd/tasks/task_custom_fields_extractor.go
index a601112f..397ee6c6 100644
--- a/plugins/tapd/tasks/task_custom_fields_extractor.go
+++ b/plugins/tapd/tasks/task_custom_fields_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/plugins/tapd/tasks/worklog_collector.go b/plugins/tapd/tasks/worklog_collector.go
index dc93bea9..40e17aef 100644
--- a/plugins/tapd/tasks/worklog_collector.go
+++ b/plugins/tapd/tasks/worklog_collector.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"gorm.io/gorm"
 	"net/url"
 	"time"
@@ -49,7 +50,7 @@ func CollectWorklogs(taskCtx core.SubTaskContext) error {
 		}
 		err := db.First(&latestUpdated, clauses...)
 		if err != nil && err != gorm.ErrRecordNotFound {
-			return fmt.Errorf("failed to get latest tapd changelog record: %w", err)
+			return errors.NotFound.Wrap(err, "failed to get latest tapd changelog record")
 		}
 		if latestUpdated.Id > 0 {
 			since = (*time.Time)(latestUpdated.Created)
@@ -76,7 +77,7 @@ func CollectWorklogs(taskCtx core.SubTaskContext) error {
 		ResponseParser: GetRawMessageArrayFromResponse,
 	})
 	if err != nil {
-		logger.Error("collect worklog error:", err)
+		logger.Error(err, "collect worklog error")
 		return err
 	}
 	return collector.Execute()
diff --git a/plugins/tapd/tasks/worklog_extractor.go b/plugins/tapd/tasks/worklog_extractor.go
index d021caca..6d39a30f 100644
--- a/plugins/tapd/tasks/worklog_extractor.go
+++ b/plugins/tapd/tasks/worklog_extractor.go
@@ -19,7 +19,6 @@ package tasks
 
 import (
 	"encoding/json"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/helper"
 	"github.com/apache/incubator-devlake/plugins/tapd/models"
diff --git a/runner/db.go b/runner/db.go
index dd7ac1c1..b428b450 100644
--- a/runner/db.go
+++ b/runner/db.go
@@ -19,6 +19,7 @@ package runner
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net/url"
 	"strings"
 	"time"
@@ -67,7 +68,7 @@ func NewGormDb(config *viper.Viper, logger core.Logger) (*gorm.DB, error) {
 	}
 	dbUrl := config.GetString("DB_URL")
 	if dbUrl == "" {
-		return nil, fmt.Errorf("DB_URL is required")
+		return nil, errors.BadInput.New("DB_URL is required", errors.AsUserMessage())
 	}
 	u, err := url.Parse(dbUrl)
 	if err != nil {
@@ -76,12 +77,12 @@ func NewGormDb(config *viper.Viper, logger core.Logger) (*gorm.DB, error) {
 	var db *gorm.DB
 	switch strings.ToLower(u.Scheme) {
 	case "mysql":
-		dbUrl = fmt.Sprintf(("%s@tcp(%s)%s?%s"), u.User.String(), u.Host, u.Path, u.RawQuery)
+		dbUrl = fmt.Sprintf("%s@tcp(%s)%s?%s", u.User.String(), u.Host, u.Path, u.RawQuery)
 		db, err = gorm.Open(mysql.Open(dbUrl), dbConfig)
 	case "postgresql", "postgres", "pg":
 		db, err = gorm.Open(postgres.Open(dbUrl), dbConfig)
 	default:
-		return nil, fmt.Errorf("invalid DB_URL:%s", dbUrl)
+		return nil, errors.BadInput.New(fmt.Sprintf("invalid DB_URL:%s", dbUrl), errors.AsUserMessage())
 	}
 	if err != nil {
 		return nil, err
diff --git a/runner/directrun.go b/runner/directrun.go
index df88530d..c8acc1bf 100644
--- a/runner/directrun.go
+++ b/runner/directrun.go
@@ -19,7 +19,7 @@ package runner
 
 import (
 	"context"
-	"errors"
+	goerror "errors"
 	"fmt"
 	"io"
 	"os"
@@ -112,7 +112,7 @@ func createContext() context.Context {
 
 		n, err := fmt.Scan(&buf)
 		if err != nil {
-			if errors.Is(err, io.EOF) {
+			if goerror.Is(err, io.EOF) {
 				return
 			}
 			panic(err)
diff --git a/runner/loader.go b/runner/loader.go
index bcc6eb5b..81e35ac2 100644
--- a/runner/loader.go
+++ b/runner/loader.go
@@ -19,6 +19,7 @@ package runner
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"io/fs"
 	"path/filepath"
 	"plugin"
@@ -48,7 +49,7 @@ func LoadPlugins(pluginsDir string, config *viper.Viper, logger core.Logger, db
 			}
 			pluginMeta, ok := symPluginEntry.(core.PluginMeta)
 			if !ok {
-				return fmt.Errorf("%v PluginEntry must implement PluginMeta interface", pluginName)
+				return errors.Default.New(fmt.Sprintf("%s PluginEntry must implement PluginMeta interface", pluginName))
 			}
 			if plugin, ok := symPluginEntry.(core.PluginInit); ok {
 				err = plugin.Init(config, logger, db)
diff --git a/runner/run_pipeline.go b/runner/run_pipeline.go
index 5bf37d5c..36d6bea3 100644
--- a/runner/run_pipeline.go
+++ b/runner/run_pipeline.go
@@ -75,13 +75,13 @@ func RunPipeline(
 			"stage":  i + 1,
 		}).Error
 		if err != nil {
-			log.Error("update pipeline state failed: %w", err)
+			log.Error(err, "update pipeline state failed")
 			break
 		}
 		// run tasks in parallel
 		err = runTasks(row)
 		if err != nil {
-			log.Error("run tasks failed: %w", err)
+			log.Error(err, "run tasks failed")
 			return err
 		}
 		// Deprecated
@@ -91,7 +91,7 @@ func RunPipeline(
 			"finished_tasks": finishedTasks,
 		}).Error
 		if err != nil {
-			log.Error("update pipeline state failed: %w", err)
+			log.Error(err, "update pipeline state failed")
 			return err
 		}
 	}
diff --git a/runner/run_task.go b/runner/run_task.go
index b1a21abb..6d6f6026 100644
--- a/runner/run_task.go
+++ b/runner/run_task.go
@@ -21,12 +21,11 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"time"
-
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/logger"
+	"time"
 
 	"github.com/apache/incubator-devlake/config"
-	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/models"
 	"github.com/apache/incubator-devlake/utils"
 	"github.com/mitchellh/mapstructure"
@@ -52,7 +51,7 @@ func RunTask(
 		return err
 	}
 	if task.Status == models.TASK_COMPLETED {
-		return fmt.Errorf("invalid task status")
+		return errors.Default.New("invalid task status")
 	}
 	log, err := getTaskLogger(parentLogger, task)
 	if err != nil {
@@ -62,14 +61,19 @@ func RunTask(
 	// make sure task status always correct even if it panicked
 	defer func() {
 		if r := recover(); r != nil {
-			err = fmt.Errorf("run task failed with panic (%s): %v", utils.GatherCallFrames(0), r)
+			err = errors.Default.Wrap(r.(error), fmt.Sprintf("run task failed with panic (%s)", utils.GatherCallFrames(0)))
 		}
 		finishedAt := time.Now()
 		spentSeconds := finishedAt.Unix() - beganAt.Unix()
 		if err != nil {
-			subTaskName := ""
-			if pluginErr, ok := err.(*errors.SubTaskError); ok {
-				subTaskName = pluginErr.GetSubTaskName()
+			lakeErr := errors.AsLakeErrorType(err)
+			subTaskName := "unknown"
+			if lakeErr == nil {
+				//skip
+			} else if lakeErr = lakeErr.As(errors.SubtaskErr); lakeErr != nil {
+				if meta, ok := lakeErr.GetData().(*core.SubTaskMeta); ok {
+					subTaskName = meta.Name
+				}
 			}
 			dbe := db.Model(task).Updates(map[string]interface{}{
 				"status":          models.TASK_FAILED,
@@ -79,7 +83,7 @@ func RunTask(
 				"failed_sub_task": subTaskName,
 			}).Error
 			if dbe != nil {
-				log.Error("failed to finalize task status into db: %w", err)
+				log.Error(err, "failed to finalize task status into db")
 			}
 		} else {
 			err = db.Model(task).Updates(map[string]interface{}{
@@ -138,14 +142,14 @@ func RunPluginTask(
 	subtasks []string,
 	options map[string]interface{},
 	progress chan core.RunningProgress,
-) error {
+) errors.Error {
 	pluginMeta, err := core.GetPlugin(name)
 	if err != nil {
-		return err
+		return errors.Default.WrapRaw(err)
 	}
 	pluginTask, ok := pluginMeta.(core.PluginTask)
 	if !ok {
-		return fmt.Errorf("plugin %s doesn't support PluginTask interface", name)
+		return errors.Default.New(fmt.Sprintf("plugin %s doesn't support PluginTask interface", name))
 	}
 	return RunPluginSubTasks(
 		ctx,
@@ -173,7 +177,7 @@ func RunPluginSubTasks(
 	options map[string]interface{},
 	pluginTask core.PluginTask,
 	progress chan core.RunningProgress,
-) error {
+) errors.Error {
 	logger.Info("start plugin")
 	// find out all possible subtasks this plugin can offer
 	subtaskMetas := pluginTask.SubTaskMetas()
@@ -195,7 +199,7 @@ func RunPluginSubTasks(
 		var specifiedTasks []string
 		err := mapstructure.Decode(subtaskNames, &specifiedTasks)
 		if err != nil {
-			return err
+			return errors.Default.Wrap(err, "subtasks could not be decoded")
 		}
 		if len(specifiedTasks) > 0 {
 			// first, disable all subtasks
@@ -207,7 +211,7 @@ func RunPluginSubTasks(
 				if _, ok := subtasksFlag[task]; ok {
 					subtasksFlag[task] = true
 				} else {
-					return fmt.Errorf("subtask %s does not exist", task)
+					return errors.Default.New(fmt.Sprintf("subtask %s does not exist", task))
 				}
 			}
 		}
@@ -234,7 +238,7 @@ func RunPluginSubTasks(
 	}
 	taskData, err := pluginTask.PrepareTaskData(taskCtx, options)
 	if err != nil {
-		return err
+		return errors.Default.Wrap(err, fmt.Sprintf("error preparing task data for %s", name))
 	}
 	taskCtx.SetData(taskData)
 
@@ -245,7 +249,7 @@ func RunPluginSubTasks(
 		subtaskCtx, err := taskCtx.SubTaskContext(subtaskMeta.Name)
 		if err != nil {
 			// sth went wrong
-			return err
+			return errors.Default.Wrap(err, fmt.Sprintf("error getting context subtask %s", subtaskMeta.Name))
 		}
 		if subtaskCtx == nil {
 			// subtask was disabled
@@ -264,10 +268,7 @@ func RunPluginSubTasks(
 		}
 		err = runSubtask(logger, db, taskID, subtaskNumber, subtaskCtx, subtaskMeta.EntryPoint)
 		if err != nil {
-			return &errors.SubTaskError{
-				SubTaskName: subtaskMeta.Name,
-				Message:     err.Error(),
-			}
+			return errors.SubtaskErr.Wrap(err, fmt.Sprintf("subtask %s ended unexpectedly", subtaskMeta.Name), errors.WithData(&subtaskMeta))
 		}
 		taskCtx.IncProgress(1)
 	}
@@ -289,7 +290,7 @@ func UpdateProgressDetail(db *gorm.DB, logger core.Logger, taskId uint64, progre
 		pct := float32(p.Current) / float32(p.Total)
 		err := db.Model(task).Update("progress", pct).Error
 		if err != nil {
-			logger.Error("failed to update progress: %w", err)
+			logger.Error(err, "failed to update progress: %w")
 		}
 	case core.SubTaskSetProgress:
 		progressDetail.TotalRecords = p.Total
@@ -328,7 +329,7 @@ func runSubtask(
 
 func recordSubtask(logger core.Logger, db *gorm.DB, subtask *models.Subtask) {
 	if err := db.Create(&subtask).Error; err != nil {
-		logger.Error("error writing subtask %d status to DB: %v", subtask.ID, err)
+		logger.Error(err, "error writing subtask %d status to DB: %v", subtask.ID)
 	}
 }
 
diff --git a/services/blueprint.go b/services/blueprint.go
index bb72c942..6677f44a 100644
--- a/services/blueprint.go
+++ b/services/blueprint.go
@@ -20,9 +20,9 @@ package services
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"strings"
 
-	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/logger"
 	"github.com/apache/incubator-devlake/models"
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -56,7 +56,7 @@ func CreateBlueprint(blueprint *models.Blueprint) error {
 	}
 	err = ReloadBlueprints(cronManager)
 	if err != nil {
-		return errors.InternalError
+		return errors.Internal.Wrap(err, "error reloading blueprints")
 	}
 	return nil
 }
@@ -91,9 +91,9 @@ func GetBlueprint(blueprintId uint64) (*models.Blueprint, error) {
 	err := db.First(blueprint, blueprintId).Error
 	if err != nil {
 		if err == gorm.ErrRecordNotFound {
-			return nil, errors.NewNotFound("blueprint not found")
+			return nil, errors.NotFound.New("blueprint not found", errors.AsUserMessage())
 		}
-		return nil, err
+		return nil, errors.Internal.Wrap(err, "error getting the task from database", errors.AsUserMessage())
 	}
 	return blueprint, nil
 }
@@ -111,7 +111,7 @@ func validateBlueprint(blueprint *models.Blueprint) error {
 	if !blueprint.IsManual {
 		_, err = cron.ParseStandard(blueprint.CronConfig)
 		if err != nil {
-			return fmt.Errorf("invalid cronConfig: %w", err)
+			return errors.Default.Wrap(err, "invalid cronConfig")
 		}
 	}
 	if blueprint.Mode == models.BLUEPRINT_MODE_ADVANCED {
@@ -119,16 +119,16 @@ func validateBlueprint(blueprint *models.Blueprint) error {
 		err = json.Unmarshal(blueprint.Plan, &plan)
 
 		if err != nil {
-			return fmt.Errorf("invalid plan: %w", err)
+			return errors.Default.Wrap(err, "invalid plan")
 		}
 		// tasks should not be empty
 		if len(plan) == 0 || len(plan[0]) == 0 {
-			return fmt.Errorf("empty plan")
+			return errors.Default.New("empty plan")
 		}
 	} else if blueprint.Mode == models.BLUEPRINT_MODE_NORMAL {
 		blueprint.Plan, err = GeneratePlanJson(blueprint.Settings)
 		if err != nil {
-			return fmt.Errorf("invalid plan: %w", err)
+			return errors.Default.Wrap(err, "invalid plan")
 		}
 	}
 
@@ -150,7 +150,7 @@ func PatchBlueprint(id uint64, body map[string]interface{}) (*models.Blueprint,
 	}
 	// make sure mode is not being update
 	if originMode != blueprint.Mode {
-		return nil, fmt.Errorf("mode is not updatable")
+		return nil, errors.Default.New("mode is not updatable")
 	}
 	// validation
 	err = validateBlueprint(blueprint)
@@ -161,13 +161,13 @@ func PatchBlueprint(id uint64, body map[string]interface{}) (*models.Blueprint,
 	// save
 	err = db.Save(blueprint).Error
 	if err != nil {
-		return nil, errors.InternalError
+		return nil, errors.Internal.Wrap(err, "error saving blueprint")
 	}
 
 	// reload schedule
 	err = ReloadBlueprints(cronManager)
 	if err != nil {
-		return nil, errors.InternalError
+		return nil, errors.Internal.Wrap(err, "error reloading blueprints")
 	}
 	// done
 	return blueprint, nil
@@ -177,11 +177,11 @@ func PatchBlueprint(id uint64, body map[string]interface{}) (*models.Blueprint,
 func DeleteBlueprint(id uint64) error {
 	err := db.Delete(&models.Blueprint{}, "id = ?", id).Error
 	if err != nil {
-		return errors.InternalError
+		return errors.Internal.Wrap(err, fmt.Sprintf("error deleting blueprint %d", id))
 	}
 	err = ReloadBlueprints(cronManager)
 	if err != nil {
-		return errors.InternalError
+		return errors.Internal.Wrap(err, "error reloading blueprints")
 	}
 	return nil
 }
@@ -203,19 +203,19 @@ func ReloadBlueprints(c *cron.Cron) error {
 		blueprint := pp
 		plan, err := pp.UnmarshalPlan()
 		if err != nil {
-			blueprintLog.Error("created cron job failed: %s", err)
+			blueprintLog.Error(err, "created cron job failed")
 			return err
 		}
 		_, err = c.AddFunc(pp.CronConfig, func() {
 			pipeline, err := createPipelineByBlueprint(blueprint.ID, blueprint.Name, plan)
 			if err != nil {
-				blueprintLog.Error("run cron job failed: %s", err)
+				blueprintLog.Error(err, "run cron job failed")
 			} else {
 				blueprintLog.Info("Run new cron job successfully, pipeline id: %d", pipeline.ID)
 			}
 		})
 		if err != nil {
-			blueprintLog.Error("created cron job failed: %s", err)
+			blueprintLog.Error(err, "created cron job failed")
 			return err
 		}
 	}
@@ -234,7 +234,7 @@ func createPipelineByBlueprint(blueprintId uint64, name string, plan core.Pipeli
 	pipeline, err := CreatePipeline(&newPipeline)
 	// Return all created tasks to the User
 	if err != nil {
-		blueprintLog.Error("created cron job failed: %s", err)
+		blueprintLog.Error(err, "created cron job failed")
 		return nil, err
 	}
 	return pipeline, err
@@ -253,7 +253,7 @@ func GeneratePlanJson(settings json.RawMessage) (json.RawMessage, error) {
 	case "1.0.0":
 		plan, err = GeneratePlanJsonV100(bpSettings)
 	default:
-		return nil, fmt.Errorf("unknown version of blueprint settings: %s", bpSettings.Version)
+		return nil, errors.Default.New(fmt.Sprintf("unknown version of blueprint settings: %s", bpSettings.Version))
 	}
 	if err != nil {
 		return nil, err
@@ -272,7 +272,7 @@ func GeneratePlanJsonV100(settings *models.BlueprintSettings) (core.PipelinePlan
 	plans := make([]core.PipelinePlan, len(connections))
 	for i, connection := range connections {
 		if len(connection.Scope) == 0 {
-			return nil, fmt.Errorf("connections[%d].scope is empty", i)
+			return nil, errors.Default.New(fmt.Sprintf("connections[%d].scope is empty", i))
 		}
 		plugin, err := core.GetPlugin(connection.Plugin)
 		if err != nil {
@@ -284,7 +284,7 @@ func GeneratePlanJsonV100(settings *models.BlueprintSettings) (core.PipelinePlan
 				return nil, err
 			}
 		} else {
-			return nil, fmt.Errorf("plugin %s does not support blueprint protocol version 1.0.0", connection.Plugin)
+			return nil, errors.Default.New(fmt.Sprintf("plugin %s does not support blueprint protocol version 1.0.0", connection.Plugin))
 		}
 	}
 
diff --git a/services/pipeline.go b/services/pipeline.go
index e4d9612e..be37e850 100644
--- a/services/pipeline.go
+++ b/services/pipeline.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/utils"
 	"github.com/google/uuid"
 	"os"
@@ -28,7 +29,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/logger"
 	"github.com/apache/incubator-devlake/models"
 	v11 "go.temporal.io/api/enums/v1"
@@ -84,10 +84,10 @@ func pipelineServiceInit() {
 
 	var pipelineMaxParallel = cfg.GetInt64("PIPELINE_MAX_PARALLEL")
 	if pipelineMaxParallel < 0 {
-		panic(fmt.Errorf(`PIPELINE_MAX_PARALLEL should be a positive integer`))
+		panic(errors.BadInput.New(`PIPELINE_MAX_PARALLEL should be a positive integer`, errors.AsUserMessage()))
 	}
 	if pipelineMaxParallel == 0 {
-		globalPipelineLog.Warn(`pipelineMaxParallel=0 means pipeline will be run No Limit`)
+		globalPipelineLog.Warn(nil, `pipelineMaxParallel=0 means pipeline will be run No Limit`)
 		pipelineMaxParallel = 10000
 	}
 	// run pipeline with independent goroutine
@@ -111,8 +111,8 @@ func CreatePipeline(newPipeline *models.NewPipeline) (*models.Pipeline, error) {
 	// save pipeline to database
 	err := db.Create(&pipeline).Error
 	if err != nil {
-		globalPipelineLog.Error("create pipline failed: %w", err)
-		return nil, errors.InternalError
+		globalPipelineLog.Error(err, "create pipline failed")
+		return nil, errors.Internal.Wrap(err, "create pipline failed")
 	}
 
 	// create tasks accordingly
@@ -127,7 +127,7 @@ func CreatePipeline(newPipeline *models.NewPipeline) (*models.Pipeline, error) {
 			}
 			_, err := CreateTask(newTask)
 			if err != nil {
-				globalPipelineLog.Error("create task for pipeline failed: %w", err)
+				globalPipelineLog.Error(err, "create task for pipeline failed")
 				return nil, err
 			}
 			// sync task state back to pipeline
@@ -135,11 +135,11 @@ func CreatePipeline(newPipeline *models.NewPipeline) (*models.Pipeline, error) {
 		}
 	}
 	if err != nil {
-		globalPipelineLog.Error("save tasks for pipeline failed: %w", err)
-		return nil, errors.InternalError
+		globalPipelineLog.Error(err, "save tasks for pipeline failed")
+		return nil, errors.Internal.Wrap(err, "save tasks for pipeline failed")
 	}
 	if pipeline.TotalTasks == 0 {
-		return nil, fmt.Errorf("no task to run")
+		return nil, errors.Default.New("no task to run")
 	}
 
 	// update tasks state
@@ -152,8 +152,8 @@ func CreatePipeline(newPipeline *models.NewPipeline) (*models.Pipeline, error) {
 		"plan":        pipeline.Plan,
 	}).Error
 	if err != nil {
-		globalPipelineLog.Error("update pipline state failed: %w", err)
-		return nil, errors.InternalError
+		globalPipelineLog.Error(err, "update pipline state failed")
+		return nil, errors.Internal.Wrap(err, "update pipline state failed")
 	}
 
 	return pipeline, nil
@@ -194,9 +194,9 @@ func GetPipeline(pipelineId uint64) (*models.Pipeline, error) {
 	err := db.First(pipeline, pipelineId).Error
 	if err != nil {
 		if err == gorm.ErrRecordNotFound {
-			return nil, errors.NewNotFound("pipeline not found")
+			return nil, errors.NotFound.New("pipeline not found", errors.AsUserMessage())
 		}
-		return nil, err
+		return nil, errors.Internal.Wrap(err, "error getting the pipeline from database", errors.AsUserMessage())
 	}
 	return pipeline, nil
 }
@@ -243,7 +243,7 @@ func RunPipelineInQueue(pipelineMaxParallel int64) {
 			globalPipelineLog.Info("run pipeline, %d", pipeline.ID)
 			err = runPipeline(pipeline.ID)
 			if err != nil {
-				globalPipelineLog.Error("failed to run pipeline, %d: %v", pipeline.ID, err)
+				globalPipelineLog.Error(err, "failed to run pipeline %d", pipeline.ID)
 			}
 		}()
 	}
@@ -271,7 +271,7 @@ func watchTemporalPipelines() {
 					"",
 				)
 				if err != nil {
-					globalPipelineLog.Error("failed to query workflow execution: %w", err)
+					globalPipelineLog.Error(err, "failed to query workflow execution")
 					continue
 				}
 				// workflow is terminated by outsider
@@ -291,7 +291,7 @@ func watchTemporalPipelines() {
 						for hisIter.HasNext() {
 							his, err := hisIter.Next()
 							if err != nil {
-								globalPipelineLog.Error("failed to get next from workflow history iterator: %w", err)
+								globalPipelineLog.Error(err, "failed to get next from workflow history iterator")
 								continue
 							}
 							rp.Message = fmt.Sprintf("temporal event type: %v", his.GetEventType())
@@ -304,7 +304,7 @@ func watchTemporalPipelines() {
 						"finished_at": rp.FinishedAt,
 					}).Error
 					if err != nil {
-						globalPipelineLog.Error("failed to update db: %w", err)
+						globalPipelineLog.Error(err, "failed to update db")
 					}
 					continue
 				}
@@ -313,7 +313,7 @@ func watchTemporalPipelines() {
 				for _, activity := range desc.PendingActivities {
 					taskId, err := getTaskIdFromActivityId(activity.ActivityId)
 					if err != nil {
-						globalPipelineLog.Error("unable to extract task id from activity id `%s`", activity.ActivityId)
+						globalPipelineLog.Error(err, "unable to extract task id from activity id `%s`", activity.ActivityId)
 						continue
 					}
 					progressDetail := &models.TaskProgressDetail{}
@@ -329,7 +329,7 @@ func watchTemporalPipelines() {
 					lastPayload := payloads[len(payloads)-1]
 					err = dc.FromPayload(lastPayload, progressDetail)
 					if err != nil {
-						globalPipelineLog.Error("failed to unmarshal heartbeat payload: %w", err)
+						globalPipelineLog.Error(err, "failed to unmarshal heartbeat payload")
 						continue
 					}
 				}
@@ -362,7 +362,7 @@ func NotifyExternal(pipelineId uint64) error {
 		Status:     pipeline.Status,
 	})
 	if err != nil {
-		globalPipelineLog.Error("failed to send notification: %w", err)
+		globalPipelineLog.Error(err, "failed to send notification")
 		return err
 	}
 	return nil
@@ -395,7 +395,7 @@ func getPipelineLogsPath(pipeline *models.Pipeline) (string, error) {
 		return filepath.Dir(path), nil
 	}
 	if os.IsNotExist(err) {
-		return "", fmt.Errorf("logs for pipeline #%d not found. You may be missing the LOGGING_DIR setting: %w", pipeline.ID, err)
+		return "", errors.NotFound.Wrap(err, fmt.Sprintf("logs for pipeline #%d not found. You may be missing the LOGGING_DIR setting", pipeline.ID))
 	}
-	return "", fmt.Errorf("err validating logs path for pipeline #%d: %w", pipeline.ID, err)
+	return "", errors.Default.Wrap(err, fmt.Sprintf("err validating logs path for pipeline #%d", pipeline.ID))
 }
diff --git a/services/pipeline_runner.go b/services/pipeline_runner.go
index e88ce554..0faebdbc 100644
--- a/services/pipeline_runner.go
+++ b/services/pipeline_runner.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/logger"
 	"github.com/apache/incubator-devlake/models"
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -67,7 +68,7 @@ func (p *pipelineRunner) runPipelineViaTemporal() error {
 		p.logger.GetConfig(),
 	)
 	if err != nil {
-		p.logger.Error("failed to enqueue pipeline #%d into temporal", p.pipeline.ID)
+		p.logger.Error(err, "failed to enqueue pipeline #%d into temporal", p.pipeline.ID)
 		return err
 	}
 	err = workflow.Get(context.Background(), nil)
@@ -85,7 +86,7 @@ func getPipelineLogger(pipeline *models.Pipeline) core.Logger {
 	loggingPath := logger.GetPipelineLoggerPath(pipelineLogger.GetConfig(), pipeline)
 	stream, err := logger.GetFileStream(loggingPath)
 	if err != nil {
-		globalPipelineLog.Error("unable to set stream for logging pipeline %d", pipeline.ID)
+		globalPipelineLog.Error(nil, "unable to set stream for logging pipeline %d", pipeline.ID)
 	} else {
 		pipelineLogger.SetStream(&core.LoggerStreamConfig{
 			Path:   loggingPath,
@@ -112,11 +113,11 @@ func runPipeline(pipelineId uint64) error {
 		err = pipelineRun.runPipelineStandalone()
 	}
 	if err != nil {
-		err = fmt.Errorf("error running pipeline %d: %v", pipelineId, err)
+		err = errors.Default.Wrap(err, fmt.Sprintf("error running pipeline %d", pipelineId))
 	}
 	pipeline, e := GetPipeline(pipelineId)
 	if e != nil {
-		return fmt.Errorf("unable to get pipeline %d: %v", pipelineId, err)
+		return errors.Default.Wrap(err, fmt.Sprintf("unable to get pipeline %d", pipelineId))
 	}
 	// finished, update database
 	finishedAt := time.Now()
@@ -131,7 +132,7 @@ func runPipeline(pipelineId uint64) error {
 	}
 	dbe := db.Model(pipeline).Select("finished_at", "spent_seconds", "status", "message").Updates(pipeline).Error
 	if dbe != nil {
-		globalPipelineLog.Error("update pipeline state failed: %w", dbe)
+		globalPipelineLog.Error(dbe, "update pipeline state failed")
 		return dbe
 	}
 	// notify external webhook
diff --git a/services/task.go b/services/task.go
index 83944e78..05bea06b 100644
--- a/services/task.go
+++ b/services/task.go
@@ -21,12 +21,12 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"regexp"
 	"strconv"
 	"strings"
 	"sync"
 
-	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/logger"
 	"github.com/apache/incubator-devlake/models"
 	"github.com/apache/incubator-devlake/plugins/core"
@@ -54,7 +54,7 @@ func (rt *RunningTask) Add(taskId uint64, cancel context.CancelFunc) error {
 	rt.mu.Lock()
 	defer rt.mu.Unlock()
 	if _, ok := rt.tasks[taskId]; ok {
-		return fmt.Errorf("task with id %v already running", taskId)
+		return errors.Default.New(fmt.Sprintf("task with id %d already running", taskId))
 	}
 	rt.tasks[taskId] = &RunningTaskData{
 		Cancel:         cancel,
@@ -112,7 +112,7 @@ func (rt *RunningTask) Remove(taskId uint64) (context.CancelFunc, error) {
 		delete(rt.tasks, taskId)
 		return d.Cancel, nil
 	}
-	return nil, fmt.Errorf("task with id %v not found", taskId)
+	return nil, errors.NotFound.New(fmt.Sprintf("task with id %d not found", taskId))
 }
 
 var runningTasks RunningTask
@@ -155,8 +155,8 @@ func CreateTask(newTask *models.NewTask) (*models.Task, error) {
 	}
 	err = db.Save(&task).Error
 	if err != nil {
-		taskLog.Error("save task failed", err)
-		return nil, errors.InternalError
+		taskLog.Error(err, "save task failed")
+		return nil, errors.Internal.Wrap(err, "save task failed")
 	}
 	return &task, nil
 }
@@ -202,9 +202,9 @@ func GetTask(taskId uint64) (*models.Task, error) {
 	err := db.First(task, taskId).Error
 	if err != nil {
 		if err == gorm.ErrRecordNotFound {
-			return nil, errors.NewNotFound("task not found")
+			return nil, errors.NotFound.New("task not found", errors.AsUserMessage())
 		}
-		return nil, err
+		return nil, errors.Internal.Wrap(err, "error getting the task from database", errors.AsUserMessage())
 	}
 	return task, nil
 }
@@ -233,7 +233,7 @@ func runTasksStandalone(parentLogger core.Logger, taskIds []uint64) error {
 	finished := 0
 	for err = range results {
 		if err != nil {
-			taskLog.Error("task failed", err)
+			taskLog.Error(err, "task failed")
 			errs = append(errs, err.Error())
 		}
 		finished++
@@ -242,7 +242,7 @@ func runTasksStandalone(parentLogger core.Logger, taskIds []uint64) error {
 		}
 	}
 	if len(errs) > 0 {
-		err = fmt.Errorf(strings.Join(errs, "\n"))
+		err = errors.Default.New(strings.Join(errs, "\n"))
 	}
 	return err
 }
@@ -287,7 +287,7 @@ func updateTaskProgress(taskId uint64, progress chan core.RunningProgress) {
 func getTaskIdFromActivityId(activityId string) (uint64, error) {
 	submatches := activityPattern.FindStringSubmatch(activityId)
 	if len(submatches) < 2 {
-		return 0, fmt.Errorf("activityId does not match")
+		return 0, errors.Default.New("activityId does not match")
 	}
 	return strconv.ParseUint(submatches[1], 10, 64)
 }
diff --git a/utils/io.go b/utils/io.go
index 808299d1..739f6b36 100644
--- a/utils/io.go
+++ b/utils/io.go
@@ -22,6 +22,7 @@ import (
 	"compress/gzip"
 	"context"
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/viant/afs"
 	"os"
 	"path/filepath"
@@ -47,7 +48,7 @@ func CreateGZipArchive(archivePath string, sourcePaths ...string) error {
 	// now gzip it
 	err = toGzip(archivePath)
 	if err != nil {
-		return fmt.Errorf("error compressing archive to gzip: %w", err)
+		return errors.Default.Wrap(err, "error compressing archive to gzip")
 	}
 	return nil
 }
@@ -61,15 +62,15 @@ func createArchive(archiveType string, archivePath string, sourcePaths ...string
 		}
 		srcPathAbs, err := filepath.Abs(sourcePath)
 		if err != nil {
-			return fmt.Errorf("error getting absolute path of %s: %w", sourcePaths, err)
+			return errors.Default.Wrap(err, fmt.Sprintf("error getting absolute path of %s", sourcePath))
 		}
 		archivePathAbs, err := filepath.Abs(archivePath)
 		if err != nil {
-			return fmt.Errorf("error getting absolute path of %s: %w", archivePath, err)
+			return errors.Default.Wrap(err, fmt.Sprintf("error getting absolute path of %s", archivePath))
 		}
 		srcInfo, err := os.Stat(srcPathAbs)
 		if err != nil {
-			return fmt.Errorf("error getting stats of path %s: %w", srcPathAbs, err)
+			return errors.Default.Wrap(err, fmt.Sprintf("error getting stats of path %s", srcPathAbs))
 		}
 		if relativeCopy && srcInfo.IsDir() {
 			err = copyContentsToArchive(archiveType, srcPathAbs, archivePathAbs)
@@ -81,7 +82,7 @@ func createArchive(archiveType string, archivePath string, sourcePaths ...string
 			err = copyToArchive(archiveType, srcPathAbs, archivePathAbs, sourcePath)
 		}
 		if err != nil {
-			return fmt.Errorf("error trying to copy data to archive: %w", err)
+			return errors.Default.Wrap(err, "error trying to copy data to archive")
 		}
 	}
 	return nil
diff --git a/utils/network_helper.go b/utils/network_helper.go
index 3d48c372..9bcd523f 100644
--- a/utils/network_helper.go
+++ b/utils/network_helper.go
@@ -19,6 +19,7 @@ package utils
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"net"
 	"time"
 )
@@ -32,7 +33,7 @@ func CheckDNS(domain string) error {
 	if len(ips) > 0 {
 		return nil
 	}
-	return fmt.Errorf("failed to resolve host: %s", domain)
+	return errors.Default.New(fmt.Sprintf("failed to resolve host: %s", domain))
 }
 
 // CheckNetwork FIXME ...
@@ -57,9 +58,9 @@ func ResolvePort(port string, schema string) (string, error) {
 	if schema != "" {
 		port, ok := defaultPorts[schema]
 		if !ok {
-			return "", fmt.Errorf("schema %s not found", schema)
+			return "", errors.Default.New(fmt.Sprintf("schema %s not found", schema))
 		}
 		return port, nil
 	}
-	return "", fmt.Errorf("you should provide at least one of port or schema")
+	return "", errors.Default.New("you should provide at least one of port or schema")
 }
diff --git a/worker/app/logger.go b/worker/app/logger.go
index 9cd2da77..f9a2f9c6 100644
--- a/worker/app/logger.go
+++ b/worker/app/logger.go
@@ -19,7 +19,6 @@ package app
 
 import (
 	"fmt"
-
 	"github.com/apache/incubator-devlake/plugins/core"
 	"go.temporal.io/sdk/log"
 )
diff --git a/worker/app/pipeline_workflow.go b/worker/app/pipeline_workflow.go
index ad80a984..5ff420c6 100644
--- a/worker/app/pipeline_workflow.go
+++ b/worker/app/pipeline_workflow.go
@@ -19,6 +19,7 @@ package app
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"strings"
 	"time"
@@ -44,7 +45,7 @@ func DevLakePipelineWorkflow(ctx workflow.Context, configJson []byte, pipelineId
 		},
 	)
 	if err != nil {
-		log.Error("failed to execute pipeline #%d: %w", pipelineId, err)
+		log.Error(err, "failed to execute pipeline #%d", pipelineId)
 	}
 	log.Info("finished pipeline #%d", pipelineId)
 	return err
@@ -54,7 +55,7 @@ func runTasks(ctx workflow.Context, configJson []byte, taskIds []uint64, logger
 	cleanExit := false
 	defer func() {
 		if !cleanExit {
-			logger.Error("fatal error while executing task Ids: %v", taskIds)
+			logger.Error(nil, "fatal error while executing task Ids: %v", taskIds)
 		}
 	}()
 	futures := make([]workflow.Future, len(taskIds))
@@ -76,7 +77,7 @@ func runTasks(ctx workflow.Context, configJson []byte, taskIds []uint64, logger
 	}
 	cleanExit = true
 	if len(errs) > 0 {
-		return fmt.Errorf(strings.Join(errs, "\n"))
+		return errors.Default.New(strings.Join(errs, "\n"))
 	}
 	return nil
 }
diff --git a/worker/app/shared.go b/worker/app/shared.go
index 14c24df3..f4ac7b48 100644
--- a/worker/app/shared.go
+++ b/worker/app/shared.go
@@ -19,7 +19,6 @@ package app
 
 import (
 	"bytes"
-
 	"github.com/apache/incubator-devlake/logger"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/runner"
diff --git a/worker/app/task_activity.go b/worker/app/task_activity.go
index a23dd2b5..6e17b7f1 100644
--- a/worker/app/task_activity.go
+++ b/worker/app/task_activity.go
@@ -19,7 +19,6 @@ package app
 
 import (
 	"context"
-
 	"github.com/apache/incubator-devlake/models"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/runner"
@@ -44,7 +43,7 @@ func DevLakeTaskActivity(ctx context.Context, configJson []byte, taskId uint64,
 	}()
 	err = runner.RunTask(ctx, cfg, log, db, progChan, taskId)
 	if err != nil {
-		log.Error("failed to execute task #%d: %w", taskId, err)
+		log.Error(err, "failed to execute task #%d", taskId)
 	}
 	log.Info("finished task #%d", taskId)
 	return err