{"record":{"id":"174e1dab35dd1364","repo":"Tencent/WeKnora","slug":"failed-to-calculate-replace-operations-w","errorCode":null,"errorMessage":"failed to calculate replace operations: %w","messagePattern":"failed to calculate replace operations: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/knowledge_faq_import.go","lineNumber":1410,"sourceCode":"\tif kb.FAQConfig != nil && kb.FAQConfig.IndexMode != \"\" {\n\t\tindexMode = kb.FAQConfig.IndexMode\n\t}\n\n\t// 增量更新逻辑：计算需要处理的条目\n\tvar entriesToProcess []types.FAQEntryPayload\n\tvar chunksToDelete []*types.Chunk\n\tvar skippedCount int\n\n\tif payload.Mode == types.FAQBatchModeReplace {\n\t\t// Replace模式：计算需要删除、创建、更新的条目\n\t\tentriesToProcess, chunksToDelete, skippedCount, err = s.calculateReplaceOperations(\n\t\t\tctx,\n\t\t\ttenantID,\n\t\t\tfaqKnowledge.ID,\n\t\t\tpayload.Entries,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to calculate replace operations: %w\", err)\n\t\t}\n\n\t\t// 删除需要删除的chunks（包括需要更新的旧chunks）\n\t\tif len(chunksToDelete) > 0 {\n\t\t\tchunkIDsToDelete := make([]string, 0, len(chunksToDelete))\n\t\t\tfor _, chunk := range chunksToDelete {\n\t\t\t\tchunkIDsToDelete = append(chunkIDsToDelete, chunk.ID)\n\t\t\t}\n\t\t\tif err := s.chunkRepo.DeleteChunks(ctx, tenantID, chunkIDsToDelete); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to delete chunks: %w\", err)\n\t\t\t}\n\t\t\t// 删除索引\n\t\t\tif err := s.deleteFAQChunkVectors(ctx, kb, faqKnowledge, chunksToDelete); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to delete chunk vectors: %w\", err)\n\t\t\t}\n\t\t\tlogger.Infof(ctx, \"FAQ import task %s: deleted %d chunks (including updates)\", taskID, len(chunksToDelete))\n\t\t}\n\t} else {","sourceCodeStart":1392,"sourceCodeEnd":1428,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/knowledge_faq_import.go#L1392-L1428","documentation":"executeFAQImport wraps any error returned by calculateReplaceOperations when computing the delete/update diff for replace-mode imports. It is a wrapper: the real cause is errors 470-472 (knowledge lookup or chunk listing failures) raised inside that function.","triggerScenarios":"Replace-mode import (payload.IsReplace) calls calculateReplaceOperations; it returns an error from GetKnowledgeByID or ListAllFAQChunksByKnowledgeID, or any internal diff computation error.","commonSituations":"DB connectivity loss during replace import; knowledge record deleted mid-flight; timeout while listing all existing chunks for a large knowledge base.","solutions":["Look for the nested cause ('failed to get knowledge' / 'failed to list existing chunks') in the wrapped error chain","Fix the underlying repository/DB issue identified by the cause","Retry the replace import once the database is healthy","Split very large replace imports to keep the diff computation within timeouts"],"exampleFix":"// before\nif err != nil {\n\treturn fmt.Errorf(\"failed to calculate replace operations: %w\", err)\n}\n// after\nif err != nil {\n\treturn fmt.Errorf(\"failed to calculate replace operations for task %s: %w\", taskID, err)\n}","handlingStrategy":"retry","validationCode":"// Verify knowledge and chunk listing succeed before invoking replace calculation\nif _, err := repo.GetKnowledgeByID(ctx, tenantID, knowledgeID); err != nil {\n\treturn fmt.Errorf(\"preflight knowledge lookup failed: %w\", err)\n}\nif _, err := chunkRepo.ListAllFAQChunksByKnowledgeID(ctx, tenantID, knowledgeID); err != nil {\n\treturn fmt.Errorf(\"preflight chunk listing failed: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"err := runReplaceImport(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"failed to calculate replace operations\") {\n\tif isTransientDBError(errors.Unwrap(err)) {\n\t\treturn retryWithBackoff(3, runReplaceImport, ctx, req)\n\t}\n\treturn err\n}\nreturn err","preventionTips":["Fix the underlying cause (errors 470-472) — this wrapper disappears with it","Keep replace imports per knowledge base serialized to avoid races","Use errors.Is/As on the wrapped chain rather than string matching where possible","Set context timeouts comfortably above diff computation time for large KBs"],"tags":["faq-import","replace-mode","error-wrapping"],"backgroundTag":"faq-import-replace-diff-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}