{"record":{"id":"3d6cd069ca7a8816","repo":"jackwener/OpenCLI","slug":"gmail-operation-response-body-was-unavailable","errorCode":null,"errorMessage":"Gmail ${operation} response body was unavailable","messagePattern":"Gmail (.+?) response body was unavailable","errorType":"error_code","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/gmail/utils.js","lineNumber":105,"sourceCode":"    .replace(/\\n{3,}/g, '\\n\\n')\n    .trim();\n}\n\nfunction parseJsonCapture(entry, operation) {\n  const status = Number(entry?.responseStatus || 0);\n  if (status === 401 || status === 403) {\n    throw new AuthRequiredError(GMAIL_HOST, `Gmail ${operation} returned HTTP ${status}`);\n  }\n  if (status !== 200) {\n    throw new CommandExecutionError(`Gmail ${operation} returned HTTP ${status || 'unknown'}`);\n  }\n  if (entry?.responseBodyTruncated === true) {\n    throw new CommandExecutionError(`Gmail ${operation} response exceeded the browser capture limit`);\n  }\n  const body = entry?.responsePreview;\n  if (Array.isArray(body)) return body;\n  if (typeof body !== 'string') {\n    throw new CommandExecutionError(`Gmail ${operation} response body was unavailable`);\n  }\n  try {\n    const parsed = JSON.parse(body.replace(/^\\)\\]\\}'\\s*/, ''));\n    if (!Array.isArray(parsed)) throw new Error('not an array');\n    return parsed;\n  } catch {\n    throw new CommandExecutionError(`Gmail ${operation} returned malformed JSON`);\n  }\n}\n\nfunction addressRef(value) {\n  if (!Array.isArray(value)) return null;\n  const address = cleanString(value[1]);\n  if (!address.includes('@')) return null;\n  return { address, name: cleanString(value[2]) || null };\n}\n\nfunction senderFromSummary(message) {","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/gmail/utils.js#L87-L123","documentation":"This CommandExecutionError is thrown by parseJsonCapture when the captured Gmail /sync response entry has neither a string responsePreview nor responseBodyTruncated === true — i.e. the entry reached the parser with a status of 200 but no usable body at all. waitGmailCaptures is supposed to filter such bodyless entries, so this indicates a race where the capture was read before the response body arrived or the bridge lost the body. The library throws rather than return partial data.","triggerScenarios":"parseJsonCapture receives entry with responseStatus 200, responseBodyTruncated not true, and responsePreview being undefined/null/non-string — e.g. the settled re-read in waitGmailCaptures still returned an in-flight entry, or the bridge dropped the body of the /i/bv or /i/fd response.","commonSituations":"Slow Gmail /sync responses (large mailbox, slow network) racing the capture reads; browser bridge versions that emit status before body; capture queue drained mid-flight during pagination clicks.","solutions":["Retry the command — the capture race is often transient.","Update the browser bridge to a version that only exposes entries once status+body are complete.","If it recurs, reload Gmail in the browser and rerun so the /sync request completes fully.","Check network speed/proxy; a stalled /sync response can keep the body unavailable past the 10s wait."],"exampleFix":"// user retry wrapper\ntry {\n  threads = await queryThreads(page, query, { limit: 20 });\n} catch (error) {\n  if (String(error.message).includes('response body was unavailable')) {\n    await page.reload();\n    threads = await queryThreads(page, query, { limit: 20 });\n  } else throw error;\n}","handlingStrategy":"retry","validationCode":"// confirm the page is settled before running commands\nconst url = await page.getCurrentUrl();\nif (!String(url || '').startsWith('https://mail.google.com/mail/u/')) {\n  throw new Error('Gmail is not on the mailbox view; sign in / reload before running commands');\n}","typeGuard":"function hasBody(entry) {\n  return typeof entry?.responsePreview === 'string' || entry?.responseBodyTruncated === true;\n}","tryCatchPattern":"try {\n  result = await gmailSearch(query);\n} catch (error) {\n  if (String(error.message).includes('response body was unavailable')) {\n    await sleep(2000); // let the in-flight /sync response settle\n    result = await gmailSearch(query); // single retry\n  } else throw error;\n}","preventionTips":["Avoid triggering captures while Gmail is mid-navigation; let the page settle first.","Retry once on this error — it is usually a capture race, not a data problem.","Keep the browser bridge current; older versions expose entries before bodies arrive.","On flaky networks, increase tolerances by rerunning rather than scripting rapid successive calls."],"tags":["gmail","browser-bridge","missing-response-body","race-condition"],"backgroundTag":"empty-response-body","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}