{"record":{"id":"5d6934abd04ea9ab","repo":"jackwener/OpenCLI","slug":"midjourney-derived-job-submission","errorCode":null,"errorMessage":"Midjourney derived job submission","messagePattern":"Midjourney derived job submission","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/utils.js","lineNumber":509,"sourceCode":"      const id = String(row?.id || '').toLowerCase();\n      const parent = String(row?.parent_id || '').toLowerCase();\n      const enqueuedAt = Date.parse(String(row?.enqueue_time || ''));\n      return UUID_RE.test(id)\n        && !baselineIds.has(id)\n        && parent === String(parentJobId).toLowerCase()\n        && Number.isFinite(enqueuedAt)\n        && enqueuedAt >= submittedAtMs - 5000;\n    });\n    if (candidates.length === 1) return String(candidates[0].id).toLowerCase();\n    if (!(await waitForNextPoll(page, deadline, 1.5))) break;\n  } while (true);\n  if (candidates.length > 1) {\n    throw new CommandExecutionError(\n      `Midjourney action is ambiguous; ${candidates.length} derived jobs matched parent ${parentJobId}`,\n      candidates.map((row) => jobUrl(row.id)).join(', '),\n    );\n  }\n  throw new TimeoutError('Midjourney derived job submission', timeoutSeconds, `No new child job appeared for ${parentJobId}.`);\n}\n\nexport async function waitForCompletedJob(page, jobId, timeoutSeconds) {\n  const deadline = Date.now() + timeoutSeconds * 1000;\n  let lastStatus = 'unknown';\n  do {\n    const job = await fetchJobStatus(page, jobId, { allowMissing: true });\n    if (job) {\n      lastStatus = String(job.current_status || job.status || 'unknown').toLowerCase();\n      if (lastStatus === 'completed') return job;\n      if (['failed', 'cancelled', 'canceled', 'error'].includes(lastStatus)) {\n        throw new CommandExecutionError(`Midjourney job ${jobId} ended with status \"${lastStatus}\"`);\n      }\n    }\n    if (!(await waitForNextPoll(page, deadline, 2))) break;\n  } while (true);\n  throw new TimeoutError(\n    `Midjourney job ${jobId} (last status: ${lastStatus})`,","sourceCodeStart":491,"sourceCodeEnd":527,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/utils.js#L491-L527","documentation":"TimeoutError raised when the derived-job polling loop ends without any new child job appearing for the parent (candidates never reached 1 and never exceeded 1). The operation name identifies the phase and the detail names the parent job ID that produced no child.","triggerScenarios":"The deadline expires with no new child job enqueued after submittedAtMs - 5000 for parentJobId: the action click/submit silently failed, the child job was rejected server-side, or the child appeared outside the timing window (clock skew or delayed registration beyond timeoutSeconds).","commonSituations":"Midjourney UI action not actually triggering (button state/overlay issue in the driven page); very congested queue delaying child registration past the timeout; action performed on a job whose grid children were already all generated earlier (outside the window).","solutions":["Verify on midjourney.com whether the child job exists; if it does, wait on it directly by ID instead of retrying the action.","Increase timeoutSeconds to account for slow queue registration and retry the action once.","Re-open/refresh the parent job page and re-trigger the action — stale page context can make the click a no-op.","Confirm the parent job is in a state that permits the action (not failed/expired)."],"exampleFix":"// before\nawait submitDerivedAction(page, parentJobId, 'upscale', { timeoutSeconds: 20 });\n// after\nawait submitDerivedAction(page, parentJobId, 'upscale', { timeoutSeconds: 90 }); // tolerate slow child registration","handlingStrategy":"retry","validationCode":"// verify the parent is actionable before attempting a derived submission\nconst parent = await fetchJobStatus(page, parentJobId).catch(() => null);\nif (!parent) throw new Error(`Parent ${parentJobId} not found; derived action impossible.`);\nconst st = String(parent.current_status || parent.status || '').toLowerCase();\nif (st !== 'completed') throw new Error(`Parent status is ${st}; only completed jobs accept actions.`);","typeGuard":null,"tryCatchPattern":"async function deriveWithRetry(page, parentJobId, action, timeoutSeconds) {\n  try {\n    return await submitDerivedAction(page, parentJobId, action, { timeoutSeconds });\n  } catch (e) {\n    if (String(e.message).includes('derived job submission')) {\n      await page.reload({ waitUntil: 'networkidle' }); // fix stale click context\n      return submitDerivedAction(page, parentJobId, action, { timeoutSeconds: timeoutSeconds * 2 });\n    }\n    throw e;\n  }\n}","preventionTips":["Refresh/reload the parent job page before triggering actions to avoid no-op clicks","Use longer timeoutSeconds for child registration under load","Check midjourney.com for the child before retrying to avoid duplicates","Confirm parent job state permits the action (failed/expired parents cannot)"],"tags":["midjourney","timeout","derived-job"],"backgroundTag":"poll-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}