{"record":{"id":"1c7fe69af4c92df3","repo":"nexu-io/open-design","slug":"vercel-returned-a-non-json-response","errorCode":null,"errorMessage":"Vercel returned a non-JSON response.","messagePattern":"Vercel returned a non-JSON response\\.","errorType":"http","errorClass":"DeployError","httpStatus":502,"severity":"error","filePath":"apps/daemon/src/deploy.ts","lineNumber":1907,"sourceCode":"  if (Number.isFinite(totalPages) && totalPages > 0) return page < totalPages;\n  const totalCount = Number(resultInfo?.total_count);\n  const responsePerPage = Number(resultInfo?.per_page);\n  const effectivePerPage = Number.isFinite(responsePerPage) && responsePerPage > 0\n    ? responsePerPage\n    : perPage;\n  if (Number.isFinite(totalCount) && totalCount >= 0) {\n    return page * effectivePerPage < totalCount;\n  }\n  const count = Number(resultInfo?.count);\n  if (Number.isFinite(count) && count >= 0) return count >= effectivePerPage;\n  return itemCount >= perPage;\n}\n\nasync function readVercelJson(resp: Response): Promise<JsonObject> {\n  try {\n    return await resp.json() as JsonObject;\n  } catch {\n    throw new DeployError('Vercel returned a non-JSON response.', resp.status || 502);\n  }\n}\n\nfunction cloudflareError(json: JsonObject, status: number, fallback: string) {\n  const message =\n    json?.errors?.find?.((err: JsonObject) => err?.message)?.message ||\n    json?.messages?.find?.((item: JsonObject) => item?.message)?.message ||\n    json?.message ||\n    fallback ||\n    `Cloudflare request failed (${status}).`;\n  return new DeployError(message, status, json);\n}\n\nfunction isCloudflareAlreadyExists(body: unknown) {\n  const text = JSON.stringify(body || {}).toLowerCase();\n  return (\n    text.includes('already exists') ||\n    text.includes('already exist') ||","sourceCodeStart":1889,"sourceCodeEnd":1925,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/deploy.ts#L1889-L1925","documentation":"readVercelJson mirrors the Cloudflare pattern: resp.json() is called on every Vercel API response, and a parse failure is wrapped as a DeployError using the HTTP status (or 502 fallback). It means Vercel returned HTML, empty, or plain-text instead of the expected JSON body.","triggerScenarios":"Vercel's API returns a non-JSON body: a 5xx HTML error, an auth/login interstitial, a Vercel challenge page, or an empty gateway timeout response.","commonSituations":"Transient Vercel outage; token revoked surfacing a login page; corporate proxy returning HTML for api.vercel.com; rate-limit edge returning plain text.","solutions":["Retry the deploy after a short wait; transient edge errors usually clear.","Check Vercel status for an active incident.","Confirm the Vercel token is still valid.","Remove any intercepting proxy for api.vercel.com."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"async function assertVercelJsonResponse(resp: Response): Promise<void> {\n  const ct = resp.headers.get('content-type') ?? '';\n  if (!ct.includes('application/json')) {\n    throw new Error(`Expected JSON from Vercel, got ${ct || 'unknown'} (status ${resp.status}).`);\n  }\n}\n\nawait assertVercelJsonResponse(resp);","typeGuard":"function isVercelJsonError(err: unknown): boolean {\n  return err instanceof DeployError && /non-JSON response/i.test(err.message);\n}","tryCatchPattern":"async function vercelJsonWithRetry(build: () => Promise<Response>, attempts = 3): Promise<JsonObject> {\n  for (let i = 1; i <= attempts; i++) {\n    try {\n      return await readVercelJson(await build());\n    } catch (err) {\n      if (err instanceof DeployError && /non-JSON response/i.test(err.message) && i < attempts) {\n        await new Promise((r) => setTimeout(r, 1000 * i));\n        continue;\n      }\n      throw err;\n    }\n  }\n  throw new Error('unreachable');\n}","preventionTips":["Inspect response Content-Type before parsing when calling Vercel directly.","Retry non-JSON Vercel responses with backoff; they are typically transient.","Keep the Vercel token valid to avoid HTML auth pages."],"tags":["vercel","deploy","network","upstream"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}