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 threading
import urllib.error
import urllib.parse
import urllib.request
from datetime import datetime, timezone
from pathlib import Path
@ -161,7 +162,7 @@ class SyncEngine:
if server_id:
try:
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):
pass # Already deleted on server
else:
@ -181,7 +182,7 @@ class SyncEngine:
params = ""
ts = self.last_sync_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}")
memories = result.get("memories", [])
@ -321,6 +322,11 @@ class SyncEngine:
try:
self._api_request("DELETE", f"/api/memories/{server_id}")
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:
self.enqueue_delete(server_id)
return False