JiraClient._request() has three related gaps:
- Single retry, not a loop — a second consecutive 429 aborts the sync.
- Unguarded
int() on Retry-After — raises ValueError on a non-numeric value. RFC 9110
allows an HTTP-date here, so this is reachable, not theoretical.
- No 5xx or transport-error handling — both are already retried by
SlackClient._call().
Location: devrag/utils/jira_client.py:36-43
Suggested fix:
Adopt the SlackClient._call() retry structure — a bounded loop with backoff, tolerant header
parsing, and httpx.TransportError handling:
def _request(self, method: str, url: str, **kwargs) -> httpx.Response:
for attempt in range(self._max_retries + 1):
last = attempt == self._max_retries
try:
resp = self._client.request(method, url, **kwargs)
except httpx.TransportError:
if last:
raise
time.sleep(self._backoff(attempt))
continue
if resp.status_code == 429 or resp.status_code >= 500:
if last:
resp.raise_for_status()
time.sleep(self._backoff(attempt, resp.headers.get("Retry-After")))
continue
break
resp.raise_for_status()
return resp
Given all three clients need the same behavior, consider extracting one shared retry helper into
devrag/utils/http.py rather than fixing each in isolation.
Source: Surfaced by /engineer-agent audit-code (June run); re-verified by hand against HEAD d97b2eb on 2026-07-15, confidence high.
JiraClient._request()has three related gaps:int()onRetry-After— raisesValueErroron a non-numeric value. RFC 9110allows an HTTP-date here, so this is reachable, not theoretical.
SlackClient._call().Location:
devrag/utils/jira_client.py:36-43Suggested fix:
Adopt the
SlackClient._call()retry structure — a bounded loop with backoff, tolerant headerparsing, and
httpx.TransportErrorhandling:Given all three clients need the same behavior, consider extracting one shared retry helper into
devrag/utils/http.pyrather than fixing each in isolation.Source: Surfaced by
/engineer-agent audit-code(June run); re-verified by hand against HEADd97b2ebon 2026-07-15, confidence high.