Skip to content

utils: extract classify_http_error helper, refactor 3 callsites (#38)#39

Merged
PowerCreek merged 1 commit into
mainfrom
shared-http-error-classifier
May 23, 2026
Merged

utils: extract classify_http_error helper, refactor 3 callsites (#38)#39
PowerCreek merged 1 commit into
mainfrom
shared-http-error-classifier

Conversation

@PowerCreek

Copy link
Copy Markdown

Summary

Closes #38. The urllib HTTPError + URLError + OSError + TimeoutError dispatch repeats verbatim in three sites after #15/#18/#20:

  • plugins/devagentic-canvas/client.py:_request
  • plugins/devagentic-docs/client.py:_post_graphql
  • hermes_cli/doctor.py:_check_devagentic_graph

Each does the same 401/403 → auth, 404 → not_found, other → http, URLError/OSError/TimeoutError → unreachable dispatch. Only the user-facing message text differs.

Fix

Add utils.classify_http_error(exc) -> str returning one of:

Kind Triggers
HTTP_ERROR_AUTH HTTPError 401 / 403
HTTP_ERROR_NOT_FOUND HTTPError 404
HTTP_ERROR_HTTP Other HTTPError statuses
HTTP_ERROR_UNREACHABLE URLError / OSError / TimeoutError
HTTP_ERROR_UNKNOWN Anything else

Refactor the three callsites onto a single except (URLError, OSError, TimeoutError) block (HTTPError is a URLError subclass) followed by a dispatch on the classifier output. Each site keeps its own message text — user-facing strings are byte-for-byte unchanged.

Behavior change

None. Same exception coverage, same message text, same paths through the code.

Test plan

  • 11 new tests in tests/test_utils_classify_http_error.py:
    • 401, 403, 404 each classified correctly
    • Other HTTP statuses (400/422/429/500/502/503/504) → HTTP_ERROR_HTTP
    • URLError, OSError, TimeoutError, socket.timeout → unreachable
    • ValueError, RuntimeError("401 Unauthorized")HTTP_ERROR_UNKNOWN (string-match heuristics don't apply here — they belong in callsite-specific code like the Mem0 probe)
    • HTTPError-is-URLError subclass quirk: 401 must NOT be misread as unreachable
  • pytest tests/test_utils_classify_http_error.py tests/test_devagentic_{canvas,docs}_plugin.py tests/hermes_cli/test_doctor_devagentic_graph.py → 112 passed (11 new + 47 canvas + 47 docs + 6 doctor; verifies the refactored callsites still produce identical output).

Why not the Mem0 message-substring classifier?

Mem0 (#34/#35) uses string-substring matching on str(exc) because the mem0ai SDK doesn't surface HTTPError — it wraps responses into custom exceptions. That's a different taxonomy. Currently only one site uses that pattern; not yet 3 sites. Will extract if/when another SDK uses the same shape.

Filed by hermes-maintainer (PowerCreek).

The same urllib HTTPError + URLError + OSError + TimeoutError
dispatch repeats in three sites after #15/#18/#20:

  plugins/devagentic-canvas/client.py:_request
  plugins/devagentic-docs/client.py:_post_graphql
  hermes_cli/doctor.py:_check_devagentic_graph

Each site has the same if-401/403-elif-404-else branches over the
exception and the same urllib network-error fallback. Only the
message text differs per site.

Add `utils.classify_http_error(exc) -> str` returning one of:
  HTTP_ERROR_AUTH        — 401 / 403
  HTTP_ERROR_NOT_FOUND   — 404
  HTTP_ERROR_HTTP        — other HTTPError status
  HTTP_ERROR_UNREACHABLE — URLError / OSError / TimeoutError
  HTTP_ERROR_UNKNOWN     — anything else

Refactor the three callsites onto a single `except (URLError, OSError,
TimeoutError)` (HTTPError is a URLError subclass) followed by a
dispatch on the classifier output. Each site still owns its own
message text — the user-facing strings are unchanged.

Behavior change: none. Net diff is roughly even (helper + dispatch
replaces three near-identical if/elif/else blocks).

Tests:
  - 11 new tests in tests/test_utils_classify_http_error.py covering
    401, 403, 404, generic statuses (400/422/429/500/502/503/504),
    URLError, OSError, TimeoutError, socket.timeout, unrelated
    exceptions, and the HTTPError-is-URLError subclass quirk.
  - All three refactored callsites' existing test suites still pass:
    canvas (47), docs (47), doctor devagentic-graph (6) — 112 total.

Closes #38.
@PowerCreek PowerCreek merged commit ca9e084 into main May 23, 2026
@PowerCreek PowerCreek deleted the shared-http-error-classifier branch May 23, 2026 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extract shared urllib-error classifier — pattern now repeats in 3 sites

1 participant