{"record":{"id":"c487e2a70100f3c7","repo":"Tencent/WeKnora","slug":"failed-to-get-faq-import-progress-from-redis-w","errorCode":null,"errorMessage":"failed to get FAQ import progress from Redis: %w","messagePattern":"failed to get FAQ import progress from Redis: %w","errorType":"exception","errorClass":null,"httpStatus":500,"severity":"error","filePath":"internal/application/service/knowledge_faq_import.go","lineNumber":2771,"sourceCode":"\t}\n\treturn s.redisClient.Set(ctx, key, data, faqImportProgressTTL).Err()\n}\n\n// GetFAQImportProgress retrieves the progress of an FAQ import task\nfunc (s *knowledgeService) GetFAQImportProgress(ctx context.Context, taskID string) (*types.FAQImportProgress, error) {\n\tif s.redisClient == nil {\n\t\tif v, ok := s.memFAQProgress.Load(taskID); ok {\n\t\t\treturn v.(*types.FAQImportProgress), nil\n\t\t}\n\t\treturn nil, werrors.NewNotFoundError(\"FAQ import task not found\")\n\t}\n\tkey := getFAQImportProgressKey(taskID)\n\tdata, err := s.redisClient.Get(ctx, key).Bytes()\n\tif err != nil {\n\t\tif errors.Is(err, redis.Nil) {\n\t\t\treturn nil, werrors.NewNotFoundError(\"FAQ import task not found\")\n\t\t}\n\t\treturn nil, fmt.Errorf(\"failed to get FAQ import progress from Redis: %w\", err)\n\t}\n\n\tvar progress types.FAQImportProgress\n\tif err := json.Unmarshal(data, &progress); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal FAQ import progress: %w\", err)\n\t}\n\n\t// If task is completed, enrich with persisted result fields from database\n\tif progress.Status == types.FAQImportStatusCompleted && progress.KnowledgeID != \"\" {\n\t\ttenantID := ctx.Value(types.TenantIDContextKey).(uint64)\n\t\tknowledge, err := s.repo.GetKnowledgeByID(ctx, tenantID, progress.KnowledgeID)\n\t\tif err == nil && knowledge != nil {\n\t\t\tif result, err := knowledge.GetLastFAQImportResult(); err == nil && result != nil {\n\t\t\t\tprogress.SuccessCount = result.SuccessCount\n\t\t\t\tprogress.FailedCount = result.FailedCount\n\t\t\t\tprogress.PartialFailedCount = result.PartialFailedCount\n\t\t\t\tprogress.SkippedCount = result.SkippedCount\n\t\t\t\tprogress.MergedCount = result.MergedCount","sourceCodeStart":2753,"sourceCodeEnd":2789,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/knowledge_faq_import.go#L2753-L2789","documentation":"GetFAQImportProgress wraps errors from reading the FAQ import progress document in Redis (keyed by taskID), except for redis.Nil which is converted to a NotFoundError. Any other Redis failure (connection refused, timeout, cluster error) is wrapped here, so callers cannot fetch task status.","triggerScenarios":"Client polls GET progress for a task; Redis GET fails due to connection loss, read-only replica, OOM, or timeout. A missing key deliberately returns the NotFoundError instead of this error.","commonSituations":"Redis restart/failover mid-import; wrong REDIS_URL or auth in env; progress key expired (TTL) would be redis.Nil -> 404, but network errors produce this message; Redis maxmemory evictions breaking reads.","solutions":["Check Redis health and connectivity from the service (PING, logs)","Verify REDIS_URL/password configuration for the environment","Retry the progress query; transient Redis errors usually clear after failover","If the task also vanished (redis.Nil -> 404), re-run the import or check task TTL settings"],"exampleFix":"// before\ndata, err := s.redisClient.Get(ctx, key).Bytes()\nif err != nil {\n\tif errors.Is(err, redis.Nil) {\n\t\treturn nil, werrors.NewNotFoundError(\"FAQ import task not found\")\n\t}\n\treturn nil, fmt.Errorf(\"failed to get FAQ import progress from Redis: %w\", err)\n}\n// after\ndata, err := s.redisClient.Get(ctx, key).Bytes()\nif err != nil {\n\tif errors.Is(err, redis.Nil) {\n\t\treturn nil, werrors.NewNotFoundError(\"FAQ import task not found\")\n\t}\n\treturn nil, fmt.Errorf(\"failed to get FAQ import progress from Redis (task %s): %w\", taskID, err)\n}","handlingStrategy":"retry","validationCode":"// Polling client: check Redis reachability before querying task progress\nif err := redisClient.Ping(ctx).Err(); err != nil {\n\treturn nil, fmt.Errorf(\"progress store unavailable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"progress, err := svc.GetFAQImportProgress(ctx, taskID)\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to get FAQ import progress from Redis\") {\n\t\t// transient Redis error: retry with backoff, then surface 503\n\t\tif p, rerr := retryWithBackoff(3, svc.GetFAQImportProgress, ctx, taskID); rerr == nil {\n\t\t\treturn p, nil\n\t\t}\n\t\treturn nil, statusServiceUnavailable(\"progress store temporarily unavailable\")\n\t}\n\tif isNotFound(err) {\n\t\treturn nil, statusNotFound(\"FAQ import task not found\")\n\t}\n\treturn nil, err\n}","preventionTips":["Configure Redis client retries and sane timeouts for status reads","Use Redis Sentinel/Cluster with automatic failover for the progress store","Distinguish redis.Nil (task expired/missing -> 404) from transport errors (503)","Monitor Redis errors and maxmemory; set progress key TTLs long enough for slow imports"],"tags":["redis","faq-import","progress"],"backgroundTag":"redis-connection-error","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}