nexu-io/open-design · error · Error
grok video submit ${submitResp.status}: ${truncate(submitTex
Error message
grok video submit ${submitResp.status}: ${truncate(submitText, 240)} What it means
Thrown when POST {baseUrl}/videos/generations on xAI returned a non-2xx status. The message includes the HTTP status and the first 240 chars (truncated) of the response body so the operator can read xAI's error text. This is the upstream-rejection guard for the Grok video submit step, distinct from the JSON-parse error [473] that follows.
Source
Thrown at apps/daemon/src/media/index.ts:2341
};
if (ctx.imageRef && ctx.imageRef.dataUrl) {
// grok-imagine-video accepts a base64 data URI in `image` for i2v.
// Same surface as Seedance — the dispatcher already produced the
// data URL via resolveProjectImage, so we just hand it through.
body.image = ctx.imageRef.dataUrl;
}
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();View on GitHub (pinned to 5be4028344)
Solutions
- Read the truncated body in the error message — xAI states the exact rejection reason (invalid_params, content_policy, insufficient_quota).
- If 400 invalid_params: verify duration ≤ 15s and aspect ratio is in xAI's accepted set; the daemon re-clamps but a manual override could bypass it.
- If 401/403: refresh the credential — for OAuth re-run `hermes auth add xai-oauth`; for API key rotate XAI_API_KEY.
- If 429/quota: wait for reset or upgrade your xAI plan.
- If 5xx: retry with backoff; check xAI status page.
Example fix
// before — duration not clamped at the call site
body = { duration: 30 } // → HTTP 400 from xAI
// after — clamp before submit (mirrors the dispatcher's clamp)
body = { duration: Math.min(ctx.length || 5, 15) } Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-validate the Grok submit body against xAI's hard constraints so the
// request doesn't bounce off a 400.
function validateGrokSubmitBody(body: Record<string, unknown>): string[] {
const errors: string[] = [];
const dur = body.duration as number | undefined;
if (typeof dur !== 'number' || dur < 1 || dur > 15) errors.push('duration must be 1..15s');
const aspect = body.aspect as string | undefined;
if (aspect && !['1:1','16:9','9:16','4:3','3:4','3:2','2:3','2:1'].includes(aspect)) errors.push(`unsupported aspect ${aspect}`);
return errors;
} Try / catch
// Classify the submit failure so callers retry only idempotent classes.
try {
await renderGrokVideo(ctx, creds, onProgress);
} catch (e) {
const msg = String((e as Error).message || e);
const m = msg.match(/grok video submit (\d+)/);
if (m) {
const status = Number(m[1]);
if (status === 429 || status >= 500) { await renderGrokVideo(ctx, creds, onProgress); return; }
if (status === 401 || status === 403) throw new Error('xAI credential invalid — re-sign-in via `hermes auth add xai-oauth` or rotate XAI_API_KEY');
}
throw e;
} Prevention
- Always clamp duration at the call site (≤15s) before building the submit body — the renderer re-clamps but a manual override can bypass it.
- Monitor xAI quota separately for video (costlier than chat); alert before exhaustion.
- Run a smoke render after every xAI API version bump — response shapes change with little notice.
When it happens
Trigger: xAI rejects the video generation request: invalid/missing auth (401/403), quota or rate-limit (422/429), malformed body — e.g. duration >15s slipped through (400), unsupported aspect ratio (400), or xAI-side 5xx.
Common situations: Duration clamping slipped (request sent with length > 15); aspect ratio xAI doesn't accept; XAI_API_KEY revoked; free-tier quota exhausted; prompt trips xAI moderation; transient xAI 5xx.
Related errors
- leonardo.ai submit ${submitResp.status}: ${truncate(submitTe
- grok poll ${pollResp.status}: ${truncate(pollText, 240)}
- grok video fetch ${dlResp.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/31439af6ae52a289.
Report an issue: GitHub.