nexu-io/open-design · error · Error
grok task ${lastStatus}: ${reason}
Error message
grok task ${lastStatus}: ${reason} What it means
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).
Source
Thrown at apps/daemon/src/media/index.ts:2396
let pollData: any;
try {
pollData = JSON.parse(pollText);
} catch {
throw new Error(`grok poll non-JSON: ${truncate(pollText, 200)}`);
}
lastStatus = pollData.status || '';
if (typeof onProgress === 'function') {
const elapsedSec = Math.round((Date.now() - startedAt) / 1000);
onProgress(`grok task ${requestId} status=${lastStatus || 'pending'} (elapsed ${elapsedSec}s)`);
}
if (lastStatus === 'done' || lastStatus === 'succeeded') {
videoUrl = pollData?.video?.url || null;
break;
}
if (lastStatus === 'failed' || lastStatus === 'expired') {
const reasonRaw = pollData?.error?.message || pollData?.error || lastStatus;
const reason = typeof reasonRaw === 'string' ? reasonRaw : JSON.stringify(reasonRaw);
throw new Error(`grok task ${lastStatus}: ${reason}`);
}
}
// Loop exited without a videoUrl. Distinguish the two reachable
// cases so operators know which lever to pull: bumping the poll
// ceiling (timeout) vs filing a bug against the upstream contract
// (status=done but no video.url).
if (!videoUrl) {
const elapsedSec = Math.round((Date.now() - startedAt) / 1000);
const ceilingSec = Math.round(maxMs / 1000);
throw new Error(
`grok video timed out after ${elapsedSec}s waiting for status=done `
+ `(last status: ${lastStatus || 'pending'}, ceiling ${ceilingSec}s). `
+ `If your jobs legitimately need longer, raise OD_GROK_VIDEO_MAX_POLL_MS.`,
);
}
}
if (!videoUrl) {View on GitHub (pinned to 5be4028344)
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.
Example fix
// before — terminal failure surfaces bare status
throw new Error(`grok task ${lastStatus}: ${reason}`);
// e.g. 'grok task failed: content_policy'
// caller — classify and retry on transient model_error
try {
await renderGrokVideo(ctx, creds);
} catch (e) {
if (/model_error|expired/.test(String(e.message))) await renderGrokVideo(ctx, creds);
else throw e;
} Defensive patterns
Strategy: try-catch
Try / catch
// Distinguish retryable terminal failures (model_error, expired) from
// non-retryable ones (content_policy) so callers retry sensibly.
try {
await renderGrokVideo(ctx, creds, onProgress);
} catch (e) {
const msg = String((e as Error).message || e);
if (/grok task (failed|expired):/.test(msg)) {
if (/model_error|expired|temporary|unknown/i.test(msg)) {
await renderGrokVideo(ctx, creds, onProgress); // one retry
return;
}
if (/content_policy|nsfw|safety/i.test(msg)) {
throw new Error('Grok rejected the prompt on content-policy grounds. Rephrase and retry.');
}
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- leonardo.ai generation failed
- no xAI credentials — sign in with your SuperGrok subscriptio
- grok image ${resp.status}: ${truncate(text, 240)}
- grok image non-JSON: ${truncate(text, 200)}
- grok image response had no data[0]
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/1c2f2a4a1608355b.
Report an issue: GitHub.