yikart/AiToEarn · error · AppException

DraftGenerationMemoryNotFound

DraftGenerationMemoryNotFound

Error message

ResponseCode.DraftGenerationMemoryNotFound

What it means

deleteItem() calls the repository to delete a draft-generation memory item by (userId, itemId) and throws AppException(ResponseCode.DraftGenerationMemoryNotFound) when no document was deleted — i.e. the item does not exist for that user.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-ai/src/core/draft-generation/draft-generation-memory.service.ts:53

  async listMemories(userId: string, contentType?: DraftGenerationMemoryContentType) {
    return await this.draftGenerationMemoryRepository.listByUserId(userId, contentType)
  }

  async getPlannerMemoryContext(userId: string, contentType: DraftGenerationMemoryContentType): Promise<PlannerMemoryContext> {
    const memory = await this.draftGenerationMemoryRepository.getByUserIdAndContentType(userId, contentType)
    if (!memory) {
      return { memoryItems: [] }
    }

    return {
      memoryItems: (memory.items ?? []).map(item => item.text),
    }
  }

  async deleteItem(userId: string, itemId: string) {
    const memory = await this.draftGenerationMemoryRepository.deleteItemByUserIdAndItemId(userId, itemId)
    if (!memory) {
      throw new AppException(ResponseCode.DraftGenerationMemoryNotFound)
    }
    return memory
  }

  async regenerateMemory(userId: string, contentType: DraftGenerationMemoryContentType, plannerModel?: string) {
    const { aiLogs, materials } = await this.listMemorySamples(userId, contentType)
    const sampleCount = aiLogs.length + materials.length
    const now = new Date()

    if (sampleCount === 0) {
      const emptyMemory = await this.draftGenerationMemoryRepository.updateByUserIdAndContentType(userId, contentType, {
        $set: { userId, contentType, items: [], sampleCount: 0, lastGeneratedAt: now },
      })
      if (!emptyMemory) {
        throw new AppException(ResponseCode.DraftGenerationMemoryNotFound)
      }
      return emptyMemory
    }

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Fetch the current memory list first and delete only existing item ids
  2. Handle DraftGenerationMemoryNotFound idempotently on the client (treat second delete as success)
  3. Verify the itemId and userId pairing — ids are scoped per user

Example fix

// before
await memoryApi.deleteItem(itemId) // throws if already gone
// after
try { await memoryApi.deleteItem(itemId) } catch (e) { if (e.code !== 'DraftGenerationMemoryNotFound') throw e }
Defensive patterns

Strategy: try-catch

Validate before calling

const memory = await memoryRepo.getByUserIdAndItemId(userId, itemId); if (!memory) return; // already deleted, skip call

Try / catch

try { await draftGenerationMemoryService.deleteItem(userId, itemId) } catch (e) { if (e instanceof AppException && e.code === ResponseCode.DraftGenerationMemoryNotFound) return; throw e }

Prevention

When it happens

Trigger: Calling deleteItem with an itemId that was already deleted, never existed, or belongs to a different userId; stale item id in the client after another device removed it.

Common situations: Double-click delete issuing two DELETE calls; client caches an old memory list; id copied from another user's session or environment.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/c18195b9d16d48e6. Report an issue: GitHub.