{"record":{"id":"e87e8eb9df8e6a98","repo":"jackwener/OpenCLI","slug":"midjourney-cancel-request-failed-http-result-s","errorCode":null,"errorMessage":"Midjourney cancel request failed: HTTP ${result?.status ?? 0}${result?.body ? ` ${result.body}` : ''}","messagePattern":"Midjourney cancel request failed: HTTP (.+?)(.+?)` : ''\\}","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/utils.js","lineNumber":375,"sourceCode":"export async function cancelMidjourneyJob(page, jobId) {\n  let result;\n  try {\n    result = unwrapEvaluateResult(await page.evaluate(async (id) => {\n      const response = await fetch('/api/job-cancel', {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json', 'X-CSRF-Protection': '1' },\n        body: JSON.stringify({ job_id: id }),\n      });\n      return { ok: response.ok, status: response.status, body: await response.text() };\n    }, jobId));\n  } catch (error) {\n    throw new CommandExecutionError(`Midjourney cancel request failed: ${errorMessage(error)}`);\n  }\n  if (!result?.ok) {\n    if ([401, 403].includes(Number(result?.status))) {\n      throw new AuthRequiredError(MIDJOURNEY_DOMAIN, 'Log into Midjourney in Chrome, then retry.');\n    }\n    throw new CommandExecutionError(\n      `Midjourney cancel request failed: HTTP ${result?.status ?? 0}${result?.body ? ` ${result.body}` : ''}`,\n    );\n  }\n  return result;\n}\n\nexport async function getVisibleJobIds(page) {\n  const payload = unwrapEvaluateResult(await page.evaluate(() => {\n    const ids = new Set();\n    for (const link of document.querySelectorAll('a[href*=\"/jobs/\"]')) {\n      const match = String(link.getAttribute('href') || '').match(/\\/jobs\\/([0-9a-f-]{36})/i);\n      if (match) ids.add(match[1].toLowerCase());\n    }\n    return [...ids];\n  }));\n  return Array.isArray(payload) ? payload.filter((id) => UUID_RE.test(id)) : [];\n}\n","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/utils.js#L357-L393","documentation":"When the cancel request returns a non-OK status other than 401/403, cancelMidjourneyJob throws CommandExecutionError including the HTTP status and any response body. This surfaces server-side rejection (404 unknown job, 429 rate limit, 5xx) with maximum detail for debugging.","triggerScenarios":"Cancel endpoint responds 404 (job ID not found or endpoint path changed), 429 (rate limited), or 5xx (Midjourney server error); response.ok is false and status is not 401/403.","commonSituations":"Typo'd or already-cancelled job IDs (404); cancelling many jobs in rapid succession (429); Midjourney API outages or deploy regressions (5xx); endpoint path renamed in a site update.","solutions":["Read the HTTP status and body in the message: 404 → verify the job ID exists; 429 → wait and retry with backoff; 5xx → retry later or check Midjourney status.","Confirm the job is not already completed/cancelled — many endpoints reject cancelling terminal jobs.","Retry after a delay with exponential backoff for 429/5xx responses.","If 404 persists while the job is visible on midjourney.com, the cancel endpoint contract likely changed; update the adapter."],"exampleFix":"// before\nawait cancelMidjourneyJob(page, jobId);\n// after\ntry {\n  await cancelMidjourneyJob(page, jobId);\n} catch (e) {\n  if (/HTTP 429/.test(String(e.message))) {\n    await new Promise((r) => setTimeout(r, 5000));\n    await cancelMidjourneyJob(page, jobId); // backoff retry for rate limit\n  } else throw e;\n}","handlingStrategy":"retry","validationCode":"// check the job is still cancellable (not already terminal) before calling\nconst job = await fetchJobStatus(page, jobId, { allowMissing: true });\nif (!job) throw new Error(`Job ${jobId} not found — cancel would 404.`);\nconst st = String(job.current_status || job.status || '').toLowerCase();\nif (['completed', 'cancelled', 'canceled', 'failed'].includes(st)) throw new Error(`Job already ${st}; skip cancel.`);","typeGuard":"function isRetryableHttpError(err) {\n  return /HTTP (429|5\\d\\d)/.test(String(err.message));\n}","tryCatchPattern":"async function cancelWithRetry(page, jobId, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try { return await cancelMidjourneyJob(page, jobId); }\n    catch (e) {\n      if (isRetryableHttpError(e) && i < attempts - 1) {\n        await new Promise((r) => setTimeout(r, 2000 * 2 ** i)); // backoff for 429/5xx\n        continue;\n      }\n      throw e;\n    }\n  }\n}","preventionTips":["Throttle cancels to avoid 429 rate limits","Verify job IDs and terminal status before cancelling to avoid 404s","Treat 5xx as transient; retry with exponential backoff","Track Midjourney status pages/changes if 404s persist for valid jobs"],"tags":["midjourney","http-error","cancel-request"],"backgroundTag":"http-error-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}