nexu-io/open-design · error · Error
grok video non-JSON: ${truncate(submitText, 200)}
Error message
grok video non-JSON: ${truncate(submitText, 200)} What it means
Thrown when xAI's POST /videos/generations returned 2xx but the body failed JSON.parse. The server returned a non-JSON payload (HTML, plain text, empty) with a success status. The message includes the first 200 chars (truncated) so the operator can identify the unexpected content shape. Distinct from [472] which fires on non-2xx status.
Source
Thrown at apps/daemon/src/media/index.ts:2347
}
const submitResp = await fetch(`${baseUrl}/videos/generations`, withMediaRequestInit(ctx, {
method: 'POST',
headers: {
'authorization': `Bearer ${credentials.apiKey}`,
'content-type': 'application/json',
},
body: JSON.stringify(body),
}));
const submitText = await submitResp.text();
if (!submitResp.ok) {
throw new Error(`grok video submit ${submitResp.status}: ${truncate(submitText, 240)}`);
}
let submitData: any;
try {
submitData = JSON.parse(submitText);
} catch {
throw new Error(`grok video non-JSON: ${truncate(submitText, 200)}`);
}
// Two paths: (a) the API returned the finished video synchronously
// (cached/short jobs), in which case we skip polling; (b) we got an
// {id, status:'pending'} stub and need to poll GET /videos/{id}
// until status flips to done/failed/expired.
let videoUrl = submitData?.video?.url || null;
let lastStatus = submitData?.status || '';
const requestId = submitData?.id || submitData?.request_id || null;
if (!videoUrl && requestId) {
const startedAt = Date.now();
const configuredMaxMs = Number(process.env.OD_GROK_VIDEO_MAX_POLL_MS);
const maxMs =
Number.isFinite(configuredMaxMs) && configuredMaxMs >= 60_000
? configuredMaxMs
: 8 * 60 * 1000;
if (typeof onProgress === 'function') {View on GitHub (pinned to 5be4028344)
Solutions
- Inspect the truncated body in the error — HTML/doctype means gateway or maintenance page; retry shortly.
- If you set a custom baseUrl, confirm it points at 'https://api.x.ai/v1' (or your xAI deployment's JSON API root), not an HTML surface.
- Retry with exponential backoff — interstitials are usually transient.
- If persistent, capture the full response and report to xAI support.
Example fix
// before — wrong baseUrl returns HTML credentials.baseUrl = 'https://x.ai' // → 200 OK with HTML homepage → [473] // after credentials.baseUrl = 'https://api.x.ai/v1'
Defensive patterns
Strategy: try-catch
Validate before calling
// Reject a misconfigured baseUrl before it ever produces HTML responses.
function assertGrokBaseUrl(baseUrl?: string): void {
if (!baseUrl) return;
try {
const u = new URL(baseUrl);
if (u.hostname !== 'api.x.ai' && !u.hostname.endsWith('.api.x.ai')) {
throw new Error(`xAI baseUrl host looks wrong (${u.hostname}); expected api.x.ai`);
}
} catch (e) { throw e; }
} Try / catch
// Distinguish parse failure from real HTTP failure so the operator gets the right hint.
let submitData: any;
try {
submitData = JSON.parse(submitText);
} catch {
const looksLikeHtml = /^\s*<(?:!doctype|html|body)/i.test(submitText);
throw new Error(
`grok video non-JSON (${looksLikeHtml ? 'html/gateway' : 'unknown'}): ${truncate(submitText, 200)}`,
);
} Prevention
- Validate baseUrl hostname on provider config save (reject anything that isn't api.x.ai).
- Send `Accept: application/json` on all xAI requests to discourage HTML gateway responses.
- Run a smoke render after any xAI config change to catch HTML-gateway regressions early.
When it happens
Trigger: submitResp.ok is true but JSON.parse(submitText) throws — xAI (or an intermediary) returned 200 with an HTML page, a Cloudflare challenge, an empty body, or plain text. Often a transient gateway behavior.
Common situations: Cloudflare interstitial served with 200; xAI gateway returning a cached HTML error page; reverse proxy in front of api.x.ai rewriting responses; baseUrl in provider config pointing at a non-API host.
Related errors
- grok poll non-JSON: ${truncate(pollText, 200)}
- leonardo.ai non-JSON: ${truncate(submitText, 200)}
- grok video timed out after ${elapsedSec}s waiting for status
- no xAI credentials — sign in with your SuperGrok subscriptio
- grok image ${resp.status}: ${truncate(text, 240)}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/5c433d82fb546143.
Report an issue: GitHub.