{"record":{"id":"492379217baf72b9","repo":"aaif-goose/goose","slug":"response-body-is-null","errorCode":null,"errorMessage":"Response body is null","messagePattern":"Response body is null","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ui/desktop/src/utils/githubUpdater.ts","lineNumber":192,"sourceCode":"      const response = await fetch(downloadUrl);\n      const fetchDuration = Date.now() - downloadStartTime;\n      log.info(\n        `GitHubUpdater: Download response received in ${fetchDuration}ms - Status: ${response.status} ${response.statusText}`\n      );\n\n      if (!response.ok) {\n        throw new Error(`Download failed: ${response.status} ${response.statusText}`);\n      }\n\n      // Get total size from headers\n      const contentLength = response.headers.get('content-length');\n      const totalSize = contentLength ? parseInt(contentLength, 10) : 0;\n      log.info(\n        `GitHubUpdater: Content-Length: ${totalSize} bytes (${(totalSize / 1024 / 1024).toFixed(2)} MB)`\n      );\n\n      if (!response.body) {\n        throw new Error('Response body is null');\n      }\n      let lastReportedPercent = -1; // Track last reported percentage to throttle updates\n      let lastLoggedPercent = -1; // Track for logging at 10% intervals\n\n      // Read the response stream\n      log.info('GitHubUpdater: Starting to read response stream...');\n      const reader = response.body.getReader();\n      const chunks: Uint8Array[] = [];\n      let downloadedSize = 0;\n      let lastProgressTime = Date.now();\n\n      while (true) {\n        const { done, value } = await reader.read();\n        if (done) break;\n\n        chunks.push(value);\n        downloadedSize += value.length;\n","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/ui/desktop/src/utils/githubUpdater.ts#L174-L210","documentation":"After a 2xx download response, the updater requires response.body (a ReadableStream) to stream the artifact chunk-by-chunk with progress. A null body means the fetch implementation returned headers without a stream — possible with responses constructed from cache/service workers, HTTP 204/HEAD-like empties, or environments whose fetch lacks streaming. The download cannot proceed and throws before reading any bytes.","triggerScenarios":"downloadUpdate() receiving a cached or synthesized Response whose body is null; a fetch polyfill (or Electron net.fetch in odd modes) that doesn't populate body; a 204 No Content from a misconfigured redirect chain.","commonSituations":"Service-worker caches intercepting the CDN request; custom fetch wrappers that clone()/consume the body earlier; rare proxy responses that terminate without a body yet claim success.","solutions":["Bypass caches/service workers for the download URL (or hard-reload the request)","If a custom fetch wrapper is injected, ensure it forwards the original Response untouched","Retry the download — genuinely empty 2xx responses from the CDN are transient","Fall back to opening the release page and downloading manually if streaming keeps failing"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const response = await fetch(downloadUrl);\nif (response.ok && !response.body) {\n  // bypass cache or refetch with cache: 'reload'\n  response = await fetch(downloadUrl, { cache: 'reload' });\n}","typeGuard":"const hasStreamBody = (r: Response): boolean => r.body != null;","tryCatchPattern":"try {\n  await downloadArtifact(url);\n} catch (e) {\n  if (String(e).includes('Response body is null')) {\n    await downloadArtifact(url, { cache: 'reload' }); // one retry, then surface\n  } else throw e;\n}","preventionTips":["Pass cache: 'no-store' for large artifact downloads","Avoid fetch wrappers that consume/clash with the body stream","Check response.body before entering the progress loop"],"tags":["download","fetch","streaming","auto-update","network"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}