{"record":{"id":"a286724e11bd84a0","repo":"jackwener/OpenCLI","slug":"estimated-gpu-cost-estimatedminutes-minutes-exc","errorCode":null,"errorMessage":"Estimated GPU cost ${estimatedMinutes} minutes exceeds --max-minutes ${max}","messagePattern":"Estimated GPU cost (.+?) minutes exceeds --max-minutes (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/capabilities.js","lineNumber":279,"sourceCode":"  if (speed === 'relax') perJobMinutes = 0;\n  const estimatedMinutes = Number((perJobMinutes * repeat).toFixed(2));\n\n  return {\n    requestedModel,\n    effectiveModel: selectedModel,\n    routingReason,\n    resolution,\n    speed,\n    repeat,\n    estimatedMinutes,\n  };\n}\n\nexport function assertBudget(account, estimatedMinutes, maxMinutes, reserveMinutes, creditsToMinutes) {\n  const max = normalizeNonNegativeNumber(maxMinutes, 2, '--max-minutes');\n  const reserve = normalizeNonNegativeNumber(reserveMinutes, 0, '--reserve-minutes');\n  if (estimatedMinutes > max) {\n    throw new ArgumentError(`Estimated GPU cost ${estimatedMinutes} minutes exceeds --max-minutes ${max}`);\n  }\n  const remainingCredits = Number(account?.total_credits ?? account?.credits_total);\n  const remainingMinutes = creditsToMinutes(remainingCredits);\n  if (Number.isFinite(remainingMinutes) && remainingMinutes - estimatedMinutes < reserve) {\n    throw new CommandExecutionError(\n      `Midjourney budget guard would leave ${Number((remainingMinutes - estimatedMinutes).toFixed(2))} minutes, below the ${reserve}-minute reserve`,\n    );\n  }\n  return { maxMinutes: max, reserveMinutes: reserve, remainingMinutes };\n}\n\nexport function estimateAction(\n  operation,\n  { videoResolution = 'sd', batchSize = 1, sourceIsVideo = false, sourceUsesOmni = false } = {},\n) {\n  if (!ACTION_CHOICES.includes(operation)) {\n    throw new ArgumentError(`operation must be one of: ${ACTION_CHOICES.join(', ')}`);\n  }","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/capabilities.js#L261-L297","documentation":"assertBudget enforces a user-supplied ceiling (--max-minutes, default 2) on the estimated GPU minutes for a generation. This ArgumentError is thrown when estimatedMinutes (per-job cost x repeat, adjusted for turbo/hd/omni/draft/relax) exceeds that ceiling, so a job that would be unexpectedly expensive is blocked before submission.","triggerScenarios":"Calling the generate flow with estimatedMinutes > maxMinutes: e.g. --repeat 4 with hd resolution (or turbo, or omni reference) while --max-minutes is left at the default 2, or passing a non-negative but too-small --max-minutes.","commonSituations":"Forgetting to raise --max-minutes when scaling up repeat or switching to HD/Turbo/Omni; keeping the default 2-minute budget after moving from relax to fast jobs; batch scripts with fixed budgets hitting costlier models.","solutions":["Raise --max-minutes to at least the estimated cost shown in the error (e.g. --max-minutes 8).","Reduce repeat count, or drop --hd/--turbo/--oref to lower the per-job GPU cost.","Use relax speed (0 GPU minutes) if latency is acceptable and the estimate must fit a tiny budget.","Call the plan/estimate API first and set --max-minutes from estimatedMinutes programmatically."],"exampleFix":"// before\nmidjourney generate 'a cat --hd' --repeat 4  // default --max-minutes 2\n// after\nmidjourney generate 'a cat --hd' --repeat 4 --max-minutes 8","handlingStrategy":"validation","validationCode":"const planResult = await midjourney.plan({ prompt, repeat, resolution, speed });\nconst maxMinutes = Number(process.env.MAX_MINUTES ?? 2);\nif (planResult.estimatedMinutes > maxMinutes) {\n  throw new Error(`estimate ${planResult.estimatedMinutes} min exceeds budget ${maxMinutes}; raise --max-minutes or reduce repeat`);\n}","typeGuard":"function fitsBudget(planResult, maxMinutes) {\n  return typeof planResult?.estimatedMinutes === 'number' && planResult.estimatedMinutes <= maxMinutes;\n}","tryCatchPattern":"try {\n  return await generate(args);\n} catch (err) {\n  if (err instanceof ArgumentError && err.message.startsWith('Estimated GPU cost')) {\n    const est = Number(err.message.match(/cost ([\\d.]+)/)[1]);\n    const max = Number(err.message.match(/--max-minutes ([\\d.]+)/)[1]);\n    return generate({ ...args, maxMinutes: Math.ceil(est) });\n  }\n  throw err;\n}","preventionTips":["Call the plan/estimate API before generating and set --max-minutes from the estimate.","Remember turbo doubles, hd/omni raise per-job cost; re-budget when changing these flags.","Don't leave --max-minutes at the default 2 when running repeat batches.","Set --max-minutes explicitly in CI/scripts rather than relying on defaults."],"tags":["cli","budget","gpu-cost","argument-error"],"backgroundTag":"budget-limit-exceeded","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}