Skip to content

Commit 5a91c48

Browse files
author
ralphstodomingo
committed
fix: surface a clear error on non-JSON API responses instead of crashing
When a proxy, gateway or CDN returns an HTTP 200 with an HTML body (an error or interstitial page) instead of JSON, the generated SDK client JSON-parses it and throws a raw `JSON Parse error: Unrecognized token '<'`. `parseAs` falls back to "json" whenever Content-Type is missing or unrecognized, so a non-JSON body reaches the parser. The error path was already guarded; the success path was not. Guard the JSON parse in both the v1 and v2 generated clients: on a parse failure, throw an actionable error (non-JSON response, likely a proxy/gateway error page, with HTTP status + content-type) instead of the raw parse crash. Surfaced from telemetry as a recurring extension sendMessageError.
1 parent 54a8f32 commit 5a91c48

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

packages/sdk/js/src/gen/client/client.gen.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@ export const createClient = (config: Config = {}): Client => {
115115
case "blob":
116116
case "formData":
117117
case "json":
118+
try {
119+
data = await response.json()
120+
} catch {
121+
// A 200 with a non-JSON body — typically an HTML error / interstitial
122+
// page from a proxy, gateway or CDN, not the API. `parseAs` falls back
123+
// to "json" when Content-Type is missing/unrecognized, so this reaches
124+
// JSON parsing. Surface an actionable error instead of a raw
125+
// "Unrecognized token '<'" crash.
126+
throw new Error(
127+
`Expected a JSON response but received ${response.headers.get("content-type") || "an unknown content type"} ` +
128+
`(HTTP ${response.status}). This is usually a proxy or gateway error page, not the API.`,
129+
)
130+
}
131+
break
118132
case "text":
119133
data = await response[parseAs]()
120134
break

packages/sdk/js/src/v2/gen/client/client.gen.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,19 @@ export const createClient = (config: Config = {}): Client => {
169169
// Some servers return 200 with no Content-Length and empty body.
170170
// response.json() would throw; read as text and parse if non-empty.
171171
const text = await response.text()
172-
data = text ? JSON.parse(text) : {}
172+
try {
173+
data = text ? JSON.parse(text) : {}
174+
} catch {
175+
// A 200 with a non-JSON body — typically an HTML error / interstitial
176+
// page from a proxy, gateway or CDN, not the API. `parseAs` falls back
177+
// to "json" when Content-Type is missing/unrecognized, so this reaches
178+
// JSON.parse. Surface an actionable error instead of a raw
179+
// "Unrecognized token '<'" crash.
180+
throw new Error(
181+
`Expected a JSON response but received ${response.headers.get("content-type") || "an unknown content type"} ` +
182+
`(HTTP ${response.status}). This is usually a proxy or gateway error page, not the API.`,
183+
)
184+
}
173185
break
174186
}
175187
case "stream":

0 commit comments

Comments
 (0)