{"record":{"id":"7fd53883831fa225","repo":"nexu-io/open-design","slug":"grok-video-submit-returned-no-inline-video-and-no","errorCode":null,"errorMessage":"grok video submit returned no inline video and no request_id to poll (status=${lastStatus || 'unknown'})","messagePattern":"grok video submit returned no inline video and no request_id to poll \\(status=(.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":2419,"sourceCode":"    // 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) {\n    // Submit returned neither an inline video.url nor a request_id —\n    // upstream broke its own contract. Surfacing the last status helps\n    // pinpoint whether it was a transient API blip or a malformed\n    // response we should add a parser branch for.\n    throw new Error(\n      `grok video submit returned no inline video and no request_id to poll `\n      + `(status=${lastStatus || 'unknown'})`,\n    );\n  }\n\n  const dlResp = await fetch(videoUrl, withMediaRequestInit(ctx));\n  if (!dlResp.ok) throw new Error(`grok video fetch ${dlResp.status}`);\n  const arr = await dlResp.arrayBuffer();\n  const bytes = Buffer.from(arr);\n\n  return {\n    bytes,\n    providerNote: `grok/${ctx.wireModel} · ${aspectRatio} · ${durationSec}s · ${bytes.length} bytes`,\n    suggestedExt: '.mp4',\n  };\n}\n\nfunction grokAspectFor(aspect?: string): string {","sourceCodeStart":2401,"sourceCodeEnd":2437,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L2401-L2437","documentation":"Thrown when the Grok submit step returned 2xx JSON but neither an inline video.url nor a pollable id (requestId). xAI's documented contract is either (a) finished video inline, or (b) {id, status:'pending'} stub to poll; getting neither is an upstream contract break. The message interpolates the last observed status (from submitData.status) to help pinpoint whether the response was malformed or a new shape.","triggerScenarios":"submitData parses OK but submitData.video.url, submitData.id, and submitData.request_id are all falsy. Happens if xAI ships an API change introducing a third response shape, or returns a soft-success with neither asset.","commonSituations":"xAI API version mismatch (baseUrl pinned to an older/newer version); xAI adds a new asynchronous handshake shape; malformed response from an upstream bug; an intermediate proxy rewriting the response.","solutions":["Reproduce the submit call with curl to capture the full response body and identify the new shape.","If xAI added a new field for the pollable id, extend the extraction (submitData?.id || submitData?.request_id || submitData?.<newField>) in apps/daemon/src/media/index.ts around line ~2356.","Confirm baseUrl points at the xAI API version your code targets.","Retry once — a transient malformed response may not recur; if it does, file an xAI support ticket with the captured body."],"exampleFix":"// before\nconst requestId = submitData?.id || submitData?.request_id || null;\n\n// after — accept new shape + aid diagnosis\nconst requestId =\n  submitData?.id || submitData?.request_id || submitData?.data?.id || null;\nif (!videoUrl && !requestId) {\n  throw new Error(\n    `grok video submit returned no inline video and no request_id to poll `\n    + `(status=${lastStatus || 'unknown'}, body=${truncate(JSON.stringify(submitData), 240)})`,\n  );\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Narrow the parsed Grok submit response so a missing video+id is caught at the\n// boundary with a typed, debuggable error.\ninterface GrokSubmitInline { video: { url: string }; status: string }\ninterface GrokSubmitPollable { id: string; status: string }\ninterface GrokSubmitRequestId { request_id: string; status: string }\nfunction getGrokRequestId(d: unknown): string | null {\n  if (!d || typeof d !== 'object') return null;\n  const obj = d as Record<string, any>;\n  return (typeof obj.id === 'string' && obj.id) || (typeof obj.request_id === 'string' && obj.request_id) || null;\n}\nfunction getGrokInlineVideoUrl(d: unknown): string | null {\n  if (!d || typeof d !== 'object') return null;\n  const obj = d as Record<string, any>;\n  return (typeof obj.video?.url === 'string' && obj.video.url) || null;\n}","tryCatchPattern":"let videoUrl = getGrokInlineVideoUrl(submitData);\nconst requestId = getGrokRequestId(submitData);\nif (!videoUrl && !requestId) {\n  throw new Error(\n    `grok video submit returned no inline video and no request_id to poll `\n    + `(status=${(submitData as any)?.status || 'unknown'}, body=${truncate(JSON.stringify(submitData), 240)})`,\n  );\n}","preventionTips":["Pin the xAI API version in baseUrl and document which version the parser was written against.","When xAI ships API changes, update the extractor and parser together — never let one drift.","Surface the raw submit body (truncated) on contract break so future regressions are debuggable from a single occurrence."],"tags":["grok","xai","api-contract","response-shape","parse-error"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}