{"record":{"id":"2a97f844b7f051ed","repo":"Hmbown/CodeWhale","slug":"the-update-service-is-unavailable-response-status-try-again","errorCode":null,"errorMessage":"The update service is unavailable (${response.status}). Try again later.","messagePattern":"The update service is unavailable \\((.+?)\\)\\. Try again later\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/app/updates.mjs","lineNumber":45,"sourceCode":"}\nexport function newerVersion(candidate,current) {\n  const parse=value=>/^\\d+\\.\\d+\\.\\d+$/.test(value)?value.split(\".\").map(Number):null;\n  const a=parse(candidate),b=parse(current); if(!a||!b) return false;\n  for(let i=0;i<3;i++) { if(a[i]!==b[i]) return a[i]>b[i]; } return false;\n}\nexport function releaseUpdate(release,current=APP_VERSION) {\n  const version=release?.tag_name?.replace(/^v/,\"\");\n  if(!version||release.draft||release.prerelease||!newerVersion(version,current)) return {available:false,message:`You have Computer Use ${current}. No newer stable installer is available.`};\n  const name=`Codewhale-Computer-Use-${version}-macos-universal.zip`;\n  const asset=release.assets?.find(asset=>asset.name===name);\n  const url=`${repository}/releases/download/v${version}/${name}`;\n  if(!asset||asset.browser_download_url!==url||!/^sha256:[a-f0-9]{64}$/.test(asset.digest)||!Number.isSafeInteger(asset.size)||asset.size<=0||asset.size>limit) return {available:false,message:`Version ${version} has no verified macOS installer yet.`};\n  return {available:true,version,url,sha256:asset.digest.slice(7),size:asset.size,message:`Computer Use ${version} is available. Install it to restart the helper; existing computer sessions will stop.`};\n}\nexport async function checkForUpdate() {\n  const response=await fetch(\"https://api.github.com/repos/Hmbown/codewhale-cu-plugin/releases/latest\",{redirect:\"error\",headers:{Accept:\"application/vnd.github+json\",\"X-GitHub-Api-Version\":\"2022-11-28\"},signal:AbortSignal.timeout(10_000)});\n  if(response.status===404) return {available:false,message:\"No stable installer has been published yet. Your current app is unchanged.\"};\n  if(!response.ok) throw new Error(`The update service is unavailable (${response.status}). Try again later.`);\n  return releaseUpdate(JSON.parse((await responseBytes(response,1024*1024)).toString(\"utf8\")));\n}\n\n/** Inspect both ZIP headers before extraction: no links, traversal or bombs. */\nexport function validateReleaseZip(bytes) {\n  const minimum=Math.max(0,bytes.length-65557); let end=-1;\n  for(let i=bytes.length-22;i>=minimum;i--) if(bytes.readUInt32LE(i)===0x06054b50&&i+22+bytes.readUInt16LE(i+20)===bytes.length) { end=i; break; }\n  if(end<0||bytes.readUInt16LE(end+4)||bytes.readUInt16LE(end+6)) throw new Error(\"Invalid update archive.\");\n  const count=bytes.readUInt16LE(end+10); let position=bytes.readUInt32LE(end+16),total=0;\n  if(!count||count>2000||bytes.readUInt16LE(end+8)!==count||position+bytes.readUInt32LE(end+12)!==end) throw new Error(\"Invalid update archive index.\");\n  const seen=new Set();\n  for(let i=0;i<count;i++) {\n    if(position+46>end||bytes.readUInt32LE(position)!==0x02014b50) throw new Error(\"Invalid update entry.\");\n    const flags=bytes.readUInt16LE(position+8),method=bytes.readUInt16LE(position+10),length=bytes.readUInt16LE(position+28),extra=bytes.readUInt16LE(position+30),comment=bytes.readUInt16LE(position+32);\n    const name=bytes.subarray(position+46,position+46+length).toString(\"utf8\");\n    const kind=(bytes.readUInt32LE(position+38)>>>16)&0xf000,offset=bytes.readUInt32LE(position+42),compressed=bytes.readUInt32LE(position+20);\n    const size=bytes.readUInt32LE(position+24); total+=size;\n    if(flags&1||![0,8].includes(method)||![0,0x4000,0x8000].includes(kind)||total>512*1024*1024||position+46+length+extra+comment>end) throw new Error(\"Unsupported update entry.\");","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/app/updates.mjs#L27-L63","documentation":"checkForUpdate queries the GitHub releases/latest API with a 10s timeout; a 404 is treated as 'no release yet', but any other non-OK status throws this error embedding the HTTP status code. It signals the update service could not be reached successfully and the check should be retried later.","triggerScenarios":"GitHub API returning 403 (rate limit), 5xx server errors, or other non-404 non-2xx statuses on GET /repos/.../releases/latest with the pinned Accept and API-version headers.","commonSituations":"Exhausting the unauthenticated GitHub API rate limit (60/hr) behind a shared IP; GitHub incident/outage; corporate proxy returning 401/407; a network appliance blocking api.github.com.","solutions":["Wait and retry later (rate limits reset within an hour; transient outages resolve)","Check the status in the message: 403 typically means GitHub API rate limiting — authenticate or wait","Verify network/proxy access to api.github.com (curl -i https://api.github.com/rate_limit)","Check the GitHub status page if 5xx statuses persist"],"exampleFix":"// before (unauthenticated, rate-limited on shared IPs)\nfetch(url, { headers: { Accept: \"application/vnd.github+json\" } });\n// after (supply a token to lift rate limits)\nfetch(url, { headers: { Accept: \"application/vnd.github+json\", Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } });","handlingStrategy":"retry","validationCode":"const pre = await fetch(\"https://api.github.com/rate_limit\", { signal: AbortSignal.timeout(5000) });\nif (!pre.ok || pre.status !== 200) throw new Error(\"GitHub API unreachable or rate-limited before update check\");","typeGuard":null,"tryCatchPattern":"try {\n  await checkForUpdate();\n} catch (e) {\n  if (e.message.includes(\"update service is unavailable\")) {\n    await sleep(60_000); // back off, then retry\n  } else throw e;\n}","preventionTips":["Authenticate GitHub API calls to avoid the 60/hr anonymous rate limit","Add exponential backoff around checkForUpdate","Monitor GitHub status before release windows","Bypass or correctly configure proxies that tamper with api.github.com traffic"],"tags":["network","http","github-api","rate-limit"],"backgroundTag":"http-error-response","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}