{"record":{"id":"bb436fe3aec1945f","repo":"Tencent/WeKnora","slug":"ensure-memory-subject-w","errorCode":null,"errorMessage":"ensure memory subject: %w","messagePattern":"ensure memory subject: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/memory/service.go","lineNumber":345,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"check forgotten memory: %w\", err)\n\t}\n\tif !forgotten && item.SourceMessageID != \"\" && item.Origin == types.MemoryOriginExtracted {\n\t\t// Only the background path is gated this way. An explicit \"remember\n\t\t// this\" is the user asking again, and must always win.\n\t\tforgotten, err = s.repo.HasTombstoneForMessage(\n\t\t\tctx, scope, item.SourceMessageID, rejectedMessageWindow,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"check forgotten source: %w\", err)\n\t\t}\n\t}\n\tif forgotten {\n\t\tlogger.Infof(ctx, \"memory: skipped a statement the user previously deleted\")\n\t\treturn nil, ErrPreviouslyForgotten\n\t}\n\tif _, err := s.repo.EnsureSubject(ctx, scope); err != nil {\n\t\treturn nil, fmt.Errorf(\"ensure memory subject: %w\", err)\n\t}\n\n\ttopic := types.SanitizeMemoryTopic(item.Topic)\n\tnormalizedKey := types.MemoryItemKey(topic, content)\n\texisting, err := s.repo.FindActiveByKey(ctx, scope, normalizedKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"find conflicting memory: %w\", err)\n\t}\n\tif existing != nil && types.SanitizeMemoryContent(existing.Content) == content {\n\t\t// Same statement about the same topic: nothing changed, so keep the\n\t\t// original timestamps instead of churning the row on every turn.\n\t\treturn existing, nil\n\t}\n\tif existing == nil {\n\t\t// The same fact often arrives twice: once because the user said\n\t\t// \"remember ...\" and again from the background distillation, phrased\n\t\t// slightly differently (\"我们的生产库是 X\" vs \"生产库是 X\"). They get\n\t\t// different topic keys, so key matching alone lets both through and","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/memory/service.go#L327-L363","documentation":"Before inserting a memory item, write() calls repo.EnsureSubject to guarantee the memory subject/scope row exists; a failure aborts the write with this wrapped error so no orphan items are stored without their parent subject.","triggerScenarios":"Any memory write path (Remember, PromoteTopic, CreateItem, applyDecisions, mergeRedundant, observeTopics) where repo.EnsureSubject fails — DB error, FK constraint trouble, or context cancellation.","commonSituations":"Database outage mid-write; subject table locked or migrated; context deadline exceeded during slow EnsureSubject; permission/ownership issue on the scope row.","solutions":["Check database connectivity and error logs around the failure time","Verify the memory subject table exists and migrations ran","Retry the write after transient DB issues — nothing was persisted, so it is idempotent","Check for row-lock contention on the subject row from concurrent writers"],"exampleFix":"// before\n_, err := memSvc.CreateItem(ctx, scope, item) // opaque wrapped failure\n// after\nerr := withRetry(ctx, 3, func() error {\n\t_, err := memSvc.CreateItem(ctx, scope, item)\n\treturn err\n})","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isEnsureSubjectErr(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"ensure memory subject\")\n}","tryCatchPattern":"_, err := memSvc.Remember(ctx, scope, item)\nif err != nil {\n\tif isEnsureSubjectErr(err) {\n\t\treturn retryWithBackoff(ctx, 3, call) // nothing persisted; idempotent retry\n\t}\n\treturn err\n}","preventionTips":["Verify memory subject table migrations run in every environment","Use idempotent EnsureSubject (upsert) to avoid unique-constraint races","Keep context deadlines generous enough for the EnsureSubject round-trip","Watch for lock contention on subject rows with many concurrent writers"],"tags":["memory","database","write"],"backgroundTag":"database-query-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}