{"record":{"id":"02c17d38020d212d","repo":"nexu-io/open-design","slug":"grok-poll-pollresp-status-truncate-polltext","errorCode":null,"errorMessage":"grok poll ${pollResp.status}: ${truncate(pollText, 240)}","messagePattern":"grok poll (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":2376,"sourceCode":"  if (!videoUrl && requestId) {\n    const startedAt = Date.now();\n    const configuredMaxMs = Number(process.env.OD_GROK_VIDEO_MAX_POLL_MS);\n    const maxMs =\n      Number.isFinite(configuredMaxMs) && configuredMaxMs >= 60_000\n        ? configuredMaxMs\n        : 8 * 60 * 1000;\n    if (typeof onProgress === 'function') {\n      const mode = ctx.imageRef ? 'i2v' : 't2v';\n      onProgress(`grok ${mode} task ${requestId} accepted; polling status…`);\n    }\n    while (Date.now() - startedAt < maxMs) {\n      await sleep(4000);\n      const pollResp = await fetch(`${baseUrl}/videos/${encodeURIComponent(requestId)}`, withMediaRequestInit(ctx, {\n        headers: { 'authorization': `Bearer ${credentials.apiKey}` },\n      }));\n      const pollText = await pollResp.text();\n      if (!pollResp.ok) {\n        throw new Error(`grok poll ${pollResp.status}: ${truncate(pollText, 240)}`);\n      }\n      let pollData: any;\n      try {\n        pollData = JSON.parse(pollText);\n      } catch {\n        throw new Error(`grok poll non-JSON: ${truncate(pollText, 200)}`);\n      }\n      lastStatus = pollData.status || '';\n      if (typeof onProgress === 'function') {\n        const elapsedSec = Math.round((Date.now() - startedAt) / 1000);\n        onProgress(`grok task ${requestId} status=${lastStatus || 'pending'} (elapsed ${elapsedSec}s)`);\n      }\n      if (lastStatus === 'done' || lastStatus === 'succeeded') {\n        videoUrl = pollData?.video?.url || null;\n        break;\n      }\n      if (lastStatus === 'failed' || lastStatus === 'expired') {\n        const reasonRaw = pollData?.error?.message || pollData?.error || lastStatus;","sourceCodeStart":2358,"sourceCodeEnd":2394,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L2358-L2394","documentation":"Thrown inside the Grok polling loop when GET {baseUrl}/videos/{requestId} returns a non-2xx status. The message includes the HTTP status and the first 240 chars (truncated) of the poll body so the operator can read xAI's reason. Distinct from [475] (parse error), [476] (terminal failed/expired status), and [477] (timeout): this is the transport-level rejection of a single poll.","triggerScenarios":"Polling loop calls /videos/{id} and pollResp.ok is false — 401/403 (key lost scope mid-job), 404 (requestId doesn't exist or expired), 429 (rate-limit on polls), or 5xx (xAI outage). The poll loop sleeps 4000ms between attempts.","commonSituations":"Polling rate exceeded xAI's limit (429); requestId expired from xAI's retention before completion (404); API key revoked mid-job (401); transient xAI 5xx during the poll window.","solutions":["Read the truncated poll body in the error — xAI often includes a reason (rate_limited, not_found).","If 429 persistent: the 4000ms poll interval may be too aggressive for your account tier; raise the interval or wait for quota reset.","If 404: requestId is gone — resubmit the entire generation; if it 404s immediately, the submit returned a bogus id (see [478]).","If 401/403: refresh the xAI credential and resubmit.","If 5xx: retry the whole job after a short backoff."],"exampleFix":"// before — single poll failure throws immediately\nif (!pollResp.ok) {\n  throw new Error(`grok poll ${pollResp.status}: ${truncate(pollText, 240)}`);\n}\n\n// after — tolerate transient 5xx/429 within the poll ceiling\nif (!pollResp.ok) {\n  if ((pollResp.status === 429 || pollResp.status >= 500) && Date.now() - startedAt < maxMs) {\n    await sleep(8000); // back off harder\n    continue;\n  }\n  throw new Error(`grok poll ${pollResp.status}: ${truncate(pollText, 240)}`);\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Poll failures are dominated by transient 5xx/429; tolerate them inside the\n// poll window, only throw on auth or definitive not-found.\nwhile (Date.now() - startedAt < maxMs) {\n  await sleep(4000);\n  const pollResp = await fetch(`${baseUrl}/videos/${encodeURIComponent(requestId)}`, withMediaRequestInit(ctx, { headers: { authorization: `Bearer ${credentials.apiKey}` } }));\n  const pollText = await pollResp.text();\n  if (!pollResp.ok) {\n    if ((pollResp.status === 429 || pollResp.status >= 500) && Date.now() - startedAt < maxMs) { await sleep(8000); continue; }\n    throw new Error(`grok poll ${pollResp.status}: ${truncate(pollText, 240)}`);\n  }\n  // ... parse + handle\n}","preventionTips":["Treat poll 5xx and 429 as retryable inside the ceiling; the current code throws on every non-2xx.","Use a jittered backoff on 429 to avoid stampeding xAI's limiter.","Track per-requestId poll status codes in logs; chronic 4xx mid-job signals key revocation."],"tags":["grok","xai","polling","http-status","transient"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}