Skip to content

Project deletion never completes when semantic memory is disabled: the deletion worker requests the semantic service unconditionally and gives up #1575

Description

@edwinyyyu

Status 2026-10-02. PR #1584 (approved, awaiting a signed commit) removes the unguarded duplicate of _cleanup_semantic_history so the guarded definition takes effect; that covers the no-database configuration only. The complete fix is the call-site skip in MemMachine plus the refusal in the composition root, as corrected on #1600 (not a gate in the semantic getters). Tracked under #1755.

What happened

With semantic memory disabled in the server config, deleting a project never completes: the session row stays in delete status forever, the project cannot be recreated (POST /projects returns 409), and a POST /memories on it raises SessionDeletedError, which is unhandled on that route and reaches the client as a dropped connection.

Config that reproduces it (event backend on SQLite, everything else default):

semantic_memory:
  enabled: false
  config_database: profile_storage

Flow: create a project, add a few episodic memories (types: ["episodic"]), POST /projects/delete (returns 204), then POST /projects with the same ids. Server log:

[ERROR] memmachine_server.main.memmachine - Failed to delete session manual_org/proj_a
  ...
  File ".../main/memmachine.py", line 383, in _delete_session_episode_store
    await self._cleanup_semantic_history(episode_ids)
  File ".../main/memmachine.py", line 1178, in _cleanup_semantic_history
    semantic_service = await self._resources.get_semantic_service()
  ...
memmachine_server.common.errors.ResourceNotReadyError: No database configured for semantic storage.

Line numbers as of 231ce171 (main): _delete_queued_session at packages/server/src/memmachine_server/main/memmachine.py:357 gates the semantic delete on self._conf.semantic_memory.enabled (line 361), but _delete_session_episode_store (line 369) calls _cleanup_semantic_history unconditionally (line 383), and that method (line 1168) requests the semantic service unconditionally (line 1177). With semantic memory disabled there is no semantic database, so SemanticManager.get_semantic_storage (common/resource_manager/semantic_manager.py:89) raises. The worker (_delete_session_worker, line 342) logs the exception and drops the job; nothing retries it. Because delete_session (line 582) returns early once the status is Deleted, a second delete request is a no-op, so the only way out is a server restart, which re-queues deleted sessions at boot (start, line 411) and fails the same way.

Note the partial state left behind: the deletion gathers the episode-store delete, the episodic-memory delete and (when enabled) the semantic delete concurrently, so by the time the episode-store branch raises, the episodic branch may already have dropped the vector collection and segment partition. The project is then neither deleted nor intact.

Expected

A deployment with semantic memory disabled must be able to delete projects. _cleanup_semantic_history should be skipped (or be a no-op) when semantic_memory.enabled is false, mirroring the guard _delete_queued_session already applies to the semantic delete itself. Independently, a failed deletion should not leave a session in a state that no API call can revisit.

Notes

Found by a manual end-to-end run against the server with the event backend on SQLite and again on PostgreSQL; the failure is dialect-independent. The same run also showed that a search on a project in delete status is still served from the process-local instance cache with 200 responses.

Investigated and written by Claude (Claude Code), filed from the account of the user who commissioned the investigation.

Activity

  1. added theissue type on Sep 2, 2026
  2. chengyixu commented on Sep 2, 2026

    @chengyixu

    Living Memory should make deletion a durable, inspectable state transition: record project identity, enabled storage capabilities, each sub-store outcome, and the final revision instead of leaving delete as a dead end. Proactive Execution can skip disabled semantic cleanup, commit the remaining deletion atomically, and expose a retryable receipt when any branch fails; reads and writes should then fail consistently until recreation. Maker note: Klik is pre-launch, exploring Living Memory and approval-aware Proactive Execution: https://pre.hiklik.ai/?utm_source=github&utm_medium=issue_comment&utm_campaign=memory_lifecycle&utm_content=memmachine_delete_receipt_20260902

  3. edwinyyyu commented on Sep 9, 2026

    @edwinyyyu
    ContributorAuthor

    This is one seam of a wider gap: semantic_memory.enabled is consulted only at startup, and add, search and list resolve the semantic stack lazily whenever a request names the semantic type (the MCP tools always do). Filed as #1600, which also proposes gating SemanticManager.get_semantic_session_manager / get_semantic_service / get_semantic_storage on the flag. That single gate would make _cleanup_semantic_history fail fast or skip here as well, so one fix should close both.

  4. added
    recoveryA failure or crash leaves durable state nothing repairs: partial writes, lost jobs, no retry
    on Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    recoveryA failure or crash leaves durable state nothing repairs: partial writes, lost jobs, no retry

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions