{"record":{"id":"622288febe646399","repo":"mem0ai/mem0","slug":"failed-to-delete-entity-type-entity-name","errorCode":null,"errorMessage":"Failed to delete ${entity.type} ${entity.name}: ${error.message}","messagePattern":"Failed to delete (.+?) (.+?): (.+?)","errorType":"exception","errorClass":"APIError","httpStatus":null,"severity":"error","filePath":"mem0-ts/src/client/mem0.ts","lineNumber":574,"sourceCode":"    }\n\n    if (to_delete.length === 0) {\n      throw new Error(\"No entities to delete\");\n    }\n\n    for (const entity of to_delete) {\n      try {\n        // fetch() reuses the pooled connection; axios here defaulted to\n        // keepAlive: false, one handshake per entity.\n        await this._fetchWithErrorHandling(\n          `${this.host}/v2/entities/${encodePathSegment(entity.type)}/${encodePathSegment(entity.name)}/`,\n          {\n            method: \"DELETE\",\n            headers: this.headers,\n          },\n        );\n      } catch (error: any) {\n        throw new APIError(\n          `Failed to delete ${entity.type} ${entity.name}: ${error.message}`,\n        );\n      }\n    }\n\n    this._captureEvent(\"delete_users\", [\n      { userId, agentId, appId, runId, sync_type: \"sync\" },\n    ]);\n\n    return {\n      message:\n        userId || agentId || appId || runId\n          ? \"Entity deleted successfully.\"\n          : \"All users, agents, apps and runs deleted.\",\n    };\n  }\n\n  async batchUpdate(memories: Array<MemoryUpdateBody>): Promise<string> {","sourceCodeStart":556,"sourceCodeEnd":592,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0-ts/src/client/mem0.ts#L556-L592","documentation":"During the per-entity deletion loop in deleteUsers(), any failure of the DELETE call to /v2/entities/{type}/{name}/ is wrapped as APIError('Failed to delete {type} {name}: {message}'). The loop is not error-tolerant: the first failing entity aborts the whole run, and entities before it have already been deleted — the operation is intentionally not transactional.","triggerScenarios":"Bulk deleteUsers() where one entity's DELETE returns an error (already deleted by a concurrent process, permission revoked mid-run, transient 5xx, entity name with characters that changed meaning after URL encoding). The thrown message names the exact entity and the underlying error.","commonSituations":"Long bulk wipes interrupted by rate limits (429) partway through; concurrent deleters racing; partial retries re-hitting already-removed entities.","solutions":["Read the entity name and inner message from the error to retry just the remaining entities idempotently (DELETE is idempotent).","Pre-empt rate limits by chunking or throttling the delete run.","For resumable wipes, track deleted entities externally and continue from the failed one.","Re-fetch users() after a failure to reconcile actual remaining state before retrying."],"exampleFix":"// before\nawait client.deleteUsers({}); // one failure aborts the run\n\n// after\nfor (const u of (await client.users()).results) {\n  try {\n    await client.deleteUsers({ userId: u.name });\n  } catch (e) {\n    console.error(`skipped ${u.name}:`, (e as Error).message); // continue, reconcile later\n  }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const isEntityDeleteError = (e: unknown): { type: string; name: string } | null => {\n  const m = /Failed to delete (\\S+) (.+?):/.exec((e as Error).message ?? '');\n  return m ? { type: m[1], name: m[2] } : null;\n};","tryCatchPattern":"try {\n  await client.deleteUsers({});\n} catch (e) {\n  const failed = isEntityDeleteError(e);\n  if (failed) {\n    // DELETE is idempotent: re-fetch remaining entities and resume from the failed one\n    const remaining = (await client.users()).results;\n    for (const u of remaining) {\n      if (u.type === failed.type && u.name === failed.name) {\n        await client.deleteUsers({ userId: u.name } as any); // or matching scope\n      }\n    }\n  }\n  throw e;\n}","preventionTips":["Delete per-entity in your own loop with per-item try/catch so one failure cannot abort the wipe.","Throttle or chunk bulk deletes to avoid 429s mid-loop.","Because earlier entities are already deleted, always reconcile with users() before retrying."],"tags":["delete","partial-failure","bulk-operations","idempotency"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}