BUG/PRIVACY: chat deletion silently ignores provider Responses cleanup failures and performs remote cleanup synchronously #179

Open
opened 2026-09-09 15:05:26 +00:00 by nsaspy · 0 comments
Owner

Finding

Deleting a chat calls persist_chat.remove_chat(ctxid) from the async api/chat_remove.py request path. Before deleting the local chat directory, remove_chat() calls _delete_provider_responses_for_chat(ctxid).

That helper:

  1. reads/parses the persisted chat again
  2. recursively discovers stored Responses API IDs
  3. calls synchronous delete_stored_response_ids(response_ids)
  4. ignores the returned per-response error list
  5. proceeds to delete the local chat directory

delete_stored_response_ids() performs provider deletion calls sequentially and catches failures into its return value.

This creates two problems:

  • remote provider cleanup can block chat deletion on the async API path, especially for chats with many stored response IDs;
  • if any provider deletion fails, the failure is discarded and the local evidence needed to retry cleanup is immediately deleted. The UI still reports Context removed. even though provider-side stored responses may remain.

The user-facing delete action should have explicit semantics about local vs provider deletion rather than silently best-efforting privacy cleanup.

Direction

Make deletion a durable two-phase/generation-safe operation:

  • remove/deactivate the local chat promptly without synchronous remote API loops on the event thread
  • persist a cleanup tombstone/job containing only the minimum provider identifiers/metadata necessary to retry
  • run provider cleanup asynchronously with bounded concurrency/backoff
  • expose cleanup state/failure to diagnostics/UI
  • delete the tombstone only after confirmed cleanup or an explicit user/admin decision to abandon it
  • preserve the existing responses_delete_on_chat_delete=False opt-out semantics

Do not keep full deleted chat content merely to support cleanup.

Acceptance

  • Chat deletion does not synchronously make N sequential remote provider calls on the main async request loop.
  • A failed provider delete is not silently discarded.
  • Local chat data can be removed while retaining minimal durable retry metadata for provider cleanup.
  • Cleanup retries survive restart and are idempotent.
  • UI/API distinguishes local deletion success from provider cleanup pending/failed when relevant.
  • Opt-out behavior remains explicit and tested.
  • Tests cover partial failure, total provider outage, restart during cleanup, duplicate response IDs and large response-ID sets.

Mirrored from lost-rob0t/a0-symbolics#42 via tracker sync.

## Finding Deleting a chat calls `persist_chat.remove_chat(ctxid)` from the async `api/chat_remove.py` request path. Before deleting the local chat directory, `remove_chat()` calls `_delete_provider_responses_for_chat(ctxid)`. That helper: 1. reads/parses the persisted chat again 2. recursively discovers stored Responses API IDs 3. calls synchronous `delete_stored_response_ids(response_ids)` 4. ignores the returned per-response error list 5. proceeds to delete the local chat directory `delete_stored_response_ids()` performs provider deletion calls sequentially and catches failures into its return value. This creates two problems: - remote provider cleanup can block chat deletion on the async API path, especially for chats with many stored response IDs; - if any provider deletion fails, the failure is discarded and the local evidence needed to retry cleanup is immediately deleted. The UI still reports `Context removed.` even though provider-side stored responses may remain. The user-facing delete action should have explicit semantics about local vs provider deletion rather than silently best-efforting privacy cleanup. ## Direction Make deletion a durable two-phase/generation-safe operation: - remove/deactivate the local chat promptly without synchronous remote API loops on the event thread - persist a cleanup tombstone/job containing only the minimum provider identifiers/metadata necessary to retry - run provider cleanup asynchronously with bounded concurrency/backoff - expose cleanup state/failure to diagnostics/UI - delete the tombstone only after confirmed cleanup or an explicit user/admin decision to abandon it - preserve the existing `responses_delete_on_chat_delete=False` opt-out semantics Do not keep full deleted chat content merely to support cleanup. ## Acceptance - [ ] Chat deletion does not synchronously make N sequential remote provider calls on the main async request loop. - [ ] A failed provider delete is not silently discarded. - [ ] Local chat data can be removed while retaining minimal durable retry metadata for provider cleanup. - [ ] Cleanup retries survive restart and are idempotent. - [ ] UI/API distinguishes local deletion success from provider cleanup pending/failed when relevant. - [ ] Opt-out behavior remains explicit and tested. - [ ] Tests cover partial failure, total provider outage, restart during cleanup, duplicate response IDs and large response-ID sets. --- *Mirrored from [`lost-rob0t/a0-symbolics#42`](https://github.com/lost-rob0t/a0-symbolics/issues/42)* via tracker sync.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/a0-symbolics#179
No description provided.