{"record":{"id":"022022dbd3963ac5","repo":"jackwener/OpenCLI","slug":"midjourney-generation","errorCode":null,"errorMessage":"Midjourney generation","messagePattern":"Midjourney generation","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/generate.js","lineNumber":238,"sourceCode":"      );\n    }\n\n    if (!normalizeBoolean(kwargs.wait, true)) {\n      const after = await getMidjourneyAccount(page);\n      await recordQuotaSnapshot(after, 'generate-after-submit');\n      return jobIds.map((jobId) => resultBase(plan, {\n        jobId,\n        status: 'submitted',\n        observedMinutes: observedMinutes(account, after),\n        url: jobUrl(jobId),\n      }));\n    }\n\n    const jobs = [];\n    for (const jobId of jobIds) {\n      const remainingSeconds = Math.floor(timeout - (Date.now() - commandStartedAt) / 1000);\n      if (remainingSeconds < 1) {\n        throw new TimeoutError(\n          'Midjourney generation',\n          timeout,\n          `Submitted job ${jobId}; check it with \\`opencli midjourney status ${jobId}\\`.`,\n        );\n      }\n      jobs.push(await waitForCompletedJob(page, jobId, remainingSeconds));\n    }\n    const after = await getMidjourneyAccount(page);\n    await recordQuotaSnapshot(after, 'generate-after');\n    const observed = observedMinutes(account, after);\n\n    if (normalizeBoolean(kwargs['skip-download'])) {\n      return jobIds.map((jobId) => resultBase(plan, {\n        jobId,\n        status: 'completed',\n        observedMinutes: observed,\n        url: jobUrl(jobId),\n      }));","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/generate.js#L220-L256","documentation":"TimeoutError('Midjourney generation', timeout) is thrown in `opencli midjourney generate` when the shared --timeout budget is exhausted while waiting for submitted jobs to finish rendering. The job(s) were successfully submitted to Midjourney, but the overall command wall-clock deadline (default per the command's timeout arg, split across submission + per-job polling via waitForCompletedJob) ran out before every job reached 'completed'. The message includes the job id so no work is lost — the job may still complete server-side.","triggerScenarios":"Running `opencli midjourney generate` with --wait enabled (the default) where (Date.now() - commandStartedAt)/1000 >= timeout before the loop reaches a job, or a previous job in the batch consumed the entire budget leaving <1 second for the next jobId; slow Midjourney renders (large batches, --repeat, relax mode queueing) or many jobs against one shared timeout.","commonSituations":"User passes a small --timeout (e.g. 60s) while Midjourney queue is long; generating a multi-image batch where the first image takes almost the whole timeout; running in relax mode during peak hours when jobs sit queued for minutes; slow network/Browser Bridge polling adding latency.","solutions":["Re-run `opencli midjourney status <jobId>` (or `download`) with the job id from the error message — the job keeps rendering server-side and is not lost.","Retry the command with a larger --timeout (e.g. --timeout 600) so submission + rendering fit in the budget.","Generate fewer images at once (avoid --repeat / lower batch) so per-job waits fit in one timeout window.","Use --no-wait (wait=false) to return immediately after submission with 'submitted' status, then poll `midjourney status` separately.","Check Midjourney queue/subscription speed (fast vs relax mode) at midjourney.com/account if renders are consistently slow."],"exampleFix":"// before\nopencli midjourney generate \"a blue ceramic teapot\" --timeout 60\n// after\nopencli midjourney generate \"a blue ceramic teapot\" --timeout 600\n# or submit without waiting:\n# opencli midjourney generate \"a blue ceramic teapot\" --no-wait","handlingStrategy":"try-catch","validationCode":"// Before a long batch, budget the timeout against expected render time:\nconst expectedSeconds = jobCount * 90; // ~90s per image\nif (timeout < expectedSeconds) throw new Error(`--timeout ${timeout}s too small for ${jobCount} job(s); use >= ${expectedSeconds}`);","typeGuard":"function isTimeoutError(e) { return e instanceof TimeoutError || (e && e.name === 'TimeoutError' && /Midjourney generation/.test(e.message ?? '')); }","tryCatchPattern":"import { TimeoutError } from '@jackwener/opencli/errors';\ntry {\n  await opencli.midjourney.generate({ prompt, timeout: 600 });\n} catch (e) {\n  if (isTimeoutError(e)) {\n    const jobId = e.message.match(/job ([0-9a-f-]{36})/i)?.[1];\n    // job still renders server-side — poll instead of retrying the paid generation\n    return pollStatus(jobId);\n  }\n  throw e;\n}","preventionTips":["Always run generate with --no-wait when scripting, and poll `midjourney status` on your own schedule.","Set --timeout generously (600s+) relative to batch size and relax-mode queueing.","Avoid --repeat/large batches within a single timeout window; split into multiple invocations.","Capture the job id from the error message — never resubmit a timed-out prompt blindly (it costs credits)."],"tags":["timeout","cli","async-wait","midjourney"],"backgroundTag":"operation-timeout-exceeded","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}