{"record":{"id":"dad189196f8dfee5","repo":"Tencent/WeKnora","slug":"scan-for-duplicate-memory-w","errorCode":null,"errorMessage":"scan for duplicate memory: %w","messagePattern":"scan for duplicate memory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/memory/service.go","lineNumber":428,"sourceCode":"\t// and the backfill pass picks up whatever this missed.\n\ts.storeItemEmbedding(ctx, scope, cfg, stored)\n\treturn stored, nil\n}\n\n// findContainedDuplicate looks for a live memory of the same kind whose\n// statement contains, or is contained by, the incoming one.\n//\n// Containment is deliberately the whole rule. It is cheap, explainable to a\n// user reading their own memory list, and it cannot merge two statements that\n// merely share a topic — only ones where the shorter adds nothing the longer\n// does not already say. The returned bool reports whether the new statement is\n// the longer of the two.\nfunc (s *Service) findContainedDuplicate(\n\tctx context.Context, scope interfaces.MemoryScope, kind, content string,\n) (*types.MemoryItem, bool, error) {\n\tcandidates, err := s.repo.ListLive(ctx, scope, kind, 200)\n\tif err != nil {\n\t\treturn nil, false, fmt.Errorf(\"scan for duplicate memory: %w\", err)\n\t}\n\tnormalized := types.NormalizeMemoryForMatch(content)\n\tif normalized == \"\" {\n\t\treturn nil, false, nil\n\t}\n\tfor _, candidate := range candidates {\n\t\tif candidate == nil {\n\t\t\tcontinue\n\t\t}\n\t\texisting := types.NormalizeMemoryForMatch(candidate.Content)\n\t\tif existing == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(existing, normalized) {\n\t\t\treturn candidate, false, nil\n\t\t}\n\t\tif strings.Contains(normalized, existing) {\n\t\t\treturn candidate, true, nil","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/memory/service.go#L410-L446","documentation":"Wraps a failure from repo.ListLive while scanning live memory items of the same kind to find a duplicate whose content contains (or is contained by) the new content. Without the candidate list the containment-deduplication pass cannot run, so the operation aborts.","triggerScenarios":"findContainedDuplicate invoked from write when repo.ListLive(ctx, scope, kind, 200) returns an error — DB connectivity issue, timeout, or query failure on the memory items table.","commonSituations":"Heavy memory table causing ListLive timeouts; DB failover during a write; misconfigured memory scope leading to a query error.","solutions":["Check the wrapped repo error and fix the underlying ListLive query failure.","Ensure memory_items table indexes exist for scope+kind+live lookups to avoid timeouts.","Verify DB connectivity and pool settings.","Retry the write once the repository is healthy; the scan is read-only and safe to repeat."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"res, err := svc.write(ctx, scope, kind, content)\nif err != nil {\n    if errors.Is(err, context.Canceled) { return err }\n    log.Warn(\"duplicate scan failed; proceeding without containment dedup\")\n}","preventionTips":["Index memory_items on (scope, kind, live) so ListLive stays fast","Keep the live-item count per scope modest or tune the 200 limit","Watch DB slow-query logs for ListLive timeouts"],"tags":["database","memory","deduplication"],"backgroundTag":"memory-repo-lookup-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}