{"record":{"id":"a97382f42a24a860","repo":"Billionmail/BillionMail","slug":"failed-to-get-task-info-w","errorCode":null,"errorMessage":"failed to get task info: %w","messagePattern":"failed to get task info: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/batch_mail/batch_mail.go","lineNumber":531,"sourceCode":"\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn taskId, nil\n}\n\n// GetTaskInfo get task info\nfunc GetTaskInfo(ctx context.Context, taskId int) (*entity.EmailTask, error) {\n\tvar task entity.EmailTask\n\terr := g.DB().Model(\"email_tasks\").\n\t\tWhere(\"id\", taskId).\n\t\tFields(\"*, tag_ids as TagIdsRaw\").\n\t\tScan(&task)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get task info: %w\", err)\n\t}\n\n\treturn &task, nil\n}\n\n// UpdateTaskPauseStatus update task pause status\nfunc UpdateTaskPauseStatus(ctx context.Context, taskId int, isPaused bool) error {\n\tpauseValue := 0\n\tprocessValue := 1\n\n\tif isPaused {\n\t\tpauseValue = 1\n\t\tprocessValue = 3\n\t}\n\n\t_, err := g.DB().Model(\"email_tasks\").\n\t\tWhere(\"id\", taskId).\n\t\tData(g.Map{","sourceCodeStart":513,"sourceCodeEnd":549,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/batch_mail/batch_mail.go#L513-L549","documentation":"GetTaskInfo reads a single row from email_tasks (including tag_ids aliased as TagIdsRaw) and returns it. Any database error during the scan is wrapped as \"failed to get task info: %w\". Note that Scan into a struct typically does not error when no row matches — callers should also handle a zero-value task; this error specifically means the query itself failed.","triggerScenarios":"Any of DeleteTask, GetTaskMailLogs, GetTaskSendCount, PauseTask, ResumeTask, TaskInfo invoking GetTaskInfo while the SELECT on email_tasks fails: DB unreachable, connection pool exhausted, permission denied, schema drift (missing tag_ids column), or a timeout on a locked table.","commonSituations":"Postgres down or restarting while the UI polls task status; migration removed/renamed tag_ids; DB user lacks SELECT on email_tasks; heavy lock contention from bulk task updates.","solutions":["Check the wrapped underlying error and PostgreSQL health","Verify email_tasks schema includes tag_ids and matches the entity (run migrations)","Confirm DB user SELECT permissions on email_tasks","Retry after restoring connectivity; add backoff in callers that poll task info"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if taskId <= 0 {\n    return errors.New(\"invalid taskId\")\n}","typeGuard":null,"tryCatchPattern":"task, err := GetTaskInfo(ctx, taskId)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to get task info\") {\n        // check DB health, then surface a 503/retry to the caller\n    }\n    return err\n}\nif task.Id == 0 {\n    return ErrTaskNotFound // distinguish empty result from DB failure\n}","preventionTips":["Distinguish 'no row' from 'query failed' by checking the zero-value struct","Keep email_tasks schema and entity in sync via migrations","Grant SELECT on email_tasks to the app DB user","Add backoff to UI polling paths that call GetTaskInfo frequently"],"tags":["database","postgresql","query"],"backgroundTag":"database-query-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}