Tencent/WeKnora · error

check forgotten memory: %w

Error message

check forgotten memory: %w

What it means

During memory write, HasTombstone checks whether an identical statement fingerprint was previously deleted by the user; a repository failure on that check aborts the write with this wrapped error. It exists so a tombstone lookup failure can never cause a deleted memory to be silently re-added.

Source

Thrown at internal/application/service/memory/service.go:328

		if types.IsMostlyRedacted(redacted) {
			logger.Infof(ctx, "memory: dropped a statement that was mostly sensitive material")
			return nil, ErrSensitiveContent
		}
		logger.Infof(ctx, "memory: redacted sensitive material before storing")
		content = types.SanitizeMemoryContent(redacted)
	}
	if !types.IsValidMemoryKind(item.Kind) {
		item.Kind = types.MemoryKindFact
	}

	// Something the user deliberately forgot must not come back the next time
	// distillation reads the message it came from. Two checks, because the
	// re-derived statement is usually worded slightly differently and so does
	// not hash the same: the exact fingerprint, and whether the message it came
	// from already produced a memory the user rejected.
	forgotten, err := s.repo.HasTombstone(ctx, scope, types.MemoryFingerprint(content))
	if err != nil {
		return nil, fmt.Errorf("check forgotten memory: %w", err)
	}
	if !forgotten && item.SourceMessageID != "" && item.Origin == types.MemoryOriginExtracted {
		// Only the background path is gated this way. An explicit "remember
		// this" is the user asking again, and must always win.
		forgotten, err = s.repo.HasTombstoneForMessage(
			ctx, scope, item.SourceMessageID, rejectedMessageWindow,
		)
		if err != nil {
			return nil, fmt.Errorf("check forgotten source: %w", err)
		}
	}
	if forgotten {
		logger.Infof(ctx, "memory: skipped a statement the user previously deleted")
		return nil, ErrPreviouslyForgotten
	}
	if _, err := s.repo.EnsureSubject(ctx, scope); err != nil {
		return nil, fmt.Errorf("ensure memory subject: %w", err)
	}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Check database connectivity and recent DB error logs at the time of the failure
  2. Verify the tombstone schema/table exists (HasTombstone query succeeds manually)
  3. Retry the operation after the transient DB issue clears — the watermark design keeps data safe
  4. Increase DB connection pool limits if under heavy write load

Example fix

// before
item, err := memSvc.Remember(ctx, scope, item)
// after
item, err := memSvc.Remember(ctx, scope, item)
if err != nil && strings.Contains(err.Error(), "check forgotten memory") {
	time.Sleep(retryBackoff)
	item, err = memSvc.Remember(ctx, scope, item) // safe to retry; nothing was written
}
Defensive patterns

Strategy: retry

Type guard

func isTombstoneCheckErr(err error) bool {
	return err != nil && strings.Contains(err.Error(), "check forgotten memory")
}

Try / catch

item, err := memSvc.Remember(ctx, scope, item)
if err != nil {
	if isTombstoneCheckErr(err) {
		// safe to retry: write aborted before any insert
		return retryWithBackoff(ctx, 3, call)
	}
	return err
}

Prevention

When it happens

Trigger: Any write-path caller (mergeRedundant, applyDecisions, Remember, PromoteTopic, CreateItem, observeTopics) triggers write(), and repo.HasTombstone fails — typically a database error, timeout, or connection issue on the tombstone table.

Common situations: Database temporarily unavailable; migration missing the tombstones table/index; context canceled mid-query during shutdown; connection pool exhausted.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/3745f5b7b1de2c3f. Report an issue: GitHub.