fix: 404 retry loop on delete and URL-encode since param

Three fixes for high 4xx alert rate:
1. Catch urllib.error.HTTPError (not just RuntimeError) for 404 on
   DELETE in pending_ops — was causing infinite retry loop for
   already-deleted memories
2. try_sync_delete: treat 404 as success instead of enqueuing
   a retry that will also 404 forever
3. URL-encode the `since` query param to prevent `+` in timezone
   offset being decoded to a space (the asyncpg-string-timestamp
   pattern applied to the sync client)
This commit is contained in:
Viktor Barzin 2026-03-15 15:28:19 +00:00
parent 4456922294
commit e22a8f743a
No known key found for this signature in database
GPG key ID: 0EB088298288D958

View file

@ -8,6 +8,7 @@ import logging
import sqlite3 import sqlite3
import threading import threading
import urllib.error import urllib.error
import urllib.parse
import urllib.request import urllib.request
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
@ -161,7 +162,7 @@ class SyncEngine:
if server_id: if server_id:
try: try:
self._api_request("DELETE", f"/api/memories/{server_id}") self._api_request("DELETE", f"/api/memories/{server_id}")
except RuntimeError as e: except (RuntimeError, urllib.error.HTTPError) as e:
if "404" in str(e): if "404" in str(e):
pass # Already deleted on server pass # Already deleted on server
else: else:
@ -181,7 +182,7 @@ class SyncEngine:
params = "" params = ""
ts = self.last_sync_ts ts = self.last_sync_ts
if ts: if ts:
params = f"?since={ts}" params = f"?since={urllib.parse.quote(ts, safe='')}"
result = self._api_request("GET", f"/api/memories/sync{params}") result = self._api_request("GET", f"/api/memories/sync{params}")
memories = result.get("memories", []) memories = result.get("memories", [])
@ -321,6 +322,11 @@ class SyncEngine:
try: try:
self._api_request("DELETE", f"/api/memories/{server_id}") self._api_request("DELETE", f"/api/memories/{server_id}")
return True return True
except urllib.error.HTTPError as e:
if e.code == 404:
return True # Already deleted on server — not an error
self.enqueue_delete(server_id)
return False
except Exception: except Exception:
self.enqueue_delete(server_id) self.enqueue_delete(server_id)
return False return False