fix(api): check HTTP status before parsing the response body - #5
Tomauskasz wants to merge 1 commit into
Conversation
`request()` parsed every response as JSON before it looked at `res.status`.
Not every response on that socket comes from the API — a gateway, proxy or
load balancer in front of it answers 5xx with an HTML error page — so the
parse threw first and the status was lost:
monid inspect -p exa -e /search
monid: error: Unexpected token '<', "<html>\r\n<h"... is not valid JSON
The `!res.ok` branch below it already built a good message, and
`friendlyMessage()` already renders 5xx as "Something went wrong. Please try
again later." Neither could run. The status code was in hand and discarded,
so a transient upstream blip was indistinguishable from a broken CLI or a bad
endpoint — the reasonable next move is to go hunting for another provider
rather than simply retrying.
Read the body as text, attempt the parse defensively, and check the status
first so the error is driven by it. A 2xx that is not JSON is still broken,
so it now raises INVALID_RESPONSE naming the content type and a truncated
snippet, which separates a captive portal from a bad deploy.
`fetchLatestVersion()` in utils/update-check.ts already checks `res.ok`
before parsing; this brings the API client in line with it.
Signed-off-by: Tomas <180413002+Tomauskasz@users.noreply.github.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review. 📝 WalkthroughWalkthrough
ChangesAPI response handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to API responses now retain useful HTTP error handling when intermediaries return non-JSON bodies, while malformed successful responses produce clear invalid-response errors. The covered behavior is ready to merge. Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Problem
MonidAPI.request()parses every response as JSON before it checksres.status:Not every response arriving on that socket comes from the API. A gateway, proxy or load balancer in front of it answers 5xx with an HTML error page. When that happens the parse throws and the status is gone:
Everything needed to report this well was already present and unreachable — the
HTTP ${res.status}fallback on the line below,statusToCode(), andfriendlyMessage(), which renders any 5xx as "Something went wrong. Please try again later." The CLI had the status code in hand and discarded it in favour of a parse error.The practical cost is misdiagnosis. A transient 502 is indistinguishable from a broken CLI, a bad endpoint or an expired key, so the reasonable next move is to go hunting for another provider or start debugging the tool — when the correct move was to retry. I hit this against
api.monid.ai; the same call succeeded on every attempt minutes later.Worth noting
fetchLatestVersion()insrc/utils/update-check.tsalready gets the ordering right —if (!res.ok) return null;beforeres.json(). This brings the API client in line with the convention already in the codebase.Fix
res.text(), attemptJSON.parsedefensively, and check!res.okfirst so the status drives the error. A structured API error body is still preferred over the bare status when one parses, so no existing error message changes.INVALID_RESPONSEnaming the declared content type and a whitespace-collapsed 80-char snippet — enough to tell a captive portal from a bad deploy without dumping a document into the terminal.Behaviour on every well-formed response is unchanged.
Tests
New
test/api/client.test.ts, drivingrequest()throughwhoami()with a stubbedfetch:text/htmlgateway pageMonidError,HTTP_502, status502text/htmlAUTH_FAILEDBalance too lowover the statustext/htmlINVALID_RESPONSE, namestext/htmlINVALID_RESPONSE, describes it as emptyFive of the seven fail on
mainwith the original parse error; the two that pass there are the no-regression cases.Relation to #4
Independent — branched from
main, touches different files. #4 fixes a crash in theinspectrenderer; this fixes how any non-JSON response is reported. They can merge in either order.Summary by CodeRabbit