{"record":{"id":"1c2f2a4a1608355b","repo":"nexu-io/open-design","slug":"grok-task-laststatus-reason","errorCode":null,"errorMessage":"grok task ${lastStatus}: ${reason}","messagePattern":"grok task (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":2396,"sourceCode":"      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;\n        const reason = typeof reasonRaw === 'string' ? reasonRaw : JSON.stringify(reasonRaw);\n        throw new Error(`grok task ${lastStatus}: ${reason}`);\n      }\n    }\n    // Loop exited without a videoUrl. Distinguish the two reachable\n    // cases so operators know which lever to pull: bumping the poll\n    // ceiling (timeout) vs filing a bug against the upstream contract\n    // (status=done but no video.url).\n    if (!videoUrl) {\n      const elapsedSec = Math.round((Date.now() - startedAt) / 1000);\n      const ceilingSec = Math.round(maxMs / 1000);\n      throw new Error(\n        `grok video timed out after ${elapsedSec}s waiting for status=done `\n        + `(last status: ${lastStatus || 'pending'}, ceiling ${ceilingSec}s). `\n        + `If your jobs legitimately need longer, raise OD_GROK_VIDEO_MAX_POLL_MS.`,\n      );\n    }\n  }\n\n  if (!videoUrl) {","sourceCodeStart":2378,"sourceCodeEnd":2414,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L2378-L2414","documentation":"Thrown when a Grok poll returns status 'failed' or 'expired'. This is xAI's definitive terminal-failure signal — the job was accepted and then xAI failed it (content policy, model error, expiry). The message interpolates lastStatus and a derived reason string (pollData.error.message, else pollData.error JSON-stringified, else lastStatus). Distinct from [477] (no answer in time) and [474] (transport rejection).","triggerScenarios":"Polling loop receives 200 with { status: 'failed' | 'expired', error?: ... }. Caused by prompts tripping xAI moderation, upstream model crashes, or jobs that sat in the queue past xAI's expiry window.","commonSituations":"Prompt contains content xAI blocks; i2v with a reference image xAI rejects; transient model crash; job queued so long it hit xAI's expiry; safety-filter false positive.","solutions":["Read the interpolated reason in the error message — xAI's error.message usually states the cause (content_policy, model_error).","Rephrase the prompt / change the reference image for i2v and retry.","Retry once — model crashes and false positives often succeed on a second attempt.","If 'expired' and you can't make the job start faster, reduce requested duration or complexity so xAI completes before expiry.","If persistent and the prompt is benign, file an xAI support ticket with the requestId."],"exampleFix":"// before — terminal failure surfaces bare status\nthrow new Error(`grok task ${lastStatus}: ${reason}`);\n// e.g. 'grok task failed: content_policy'\n\n// caller — classify and retry on transient model_error\ntry {\n  await renderGrokVideo(ctx, creds);\n} catch (e) {\n  if (/model_error|expired/.test(String(e.message))) await renderGrokVideo(ctx, creds);\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Distinguish retryable terminal failures (model_error, expired) from\n// non-retryable ones (content_policy) so callers retry sensibly.\ntry {\n  await renderGrokVideo(ctx, creds, onProgress);\n} catch (e) {\n  const msg = String((e as Error).message || e);\n  if (/grok task (failed|expired):/.test(msg)) {\n    if (/model_error|expired|temporary|unknown/i.test(msg)) {\n      await renderGrokVideo(ctx, creds, onProgress); // one retry\n      return;\n    }\n    if (/content_policy|nsfw|safety/i.test(msg)) {\n      throw new Error('Grok rejected the prompt on content-policy grounds. Rephrase and retry.');\n    }\n  }\n  throw e;\n}","preventionTips":["Surface the upstream error.message in the thrown error (the current code already does) — classify on it rather than on a fixed string.","For i2v jobs, validate the reference image against xAI's stated constraints before submit to avoid FAILED-after-queue.","Track FAILED vs EXPIRED ratios separately; rising EXPIRED usually means upstream queueing, not bad prompts."],"tags":["grok","xai","upstream-failure","terminal-status","moderation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}