{"record":{"id":"f4777cb0d471954d","repo":"jackwener/OpenCLI","slug":"midjourney-did-not-expose-a-describe-action-for-th","errorCode":null,"errorMessage":"Midjourney did not expose a Describe action for the uploaded image","messagePattern":"Midjourney did not expose a Describe action for the uploaded image","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/describe.js","lineNumber":89,"sourceCode":"      const button = card?.querySelector('button');\n      if (!button) return false;\n      button.setAttribute('data-opencli-describe-menu', '1');\n      return true;\n    }, sourceUrl);\n    if (!menuMarked) {\n      throw new CommandExecutionError('Could not open the uploaded image action menu for Describe');\n    }\n    await page.click('[data-opencli-describe-menu=\"1\"]');\n    await page.wait(0.4);\n    const startedAt = new Date().toISOString();\n    const clicked = await page.evaluate(() => {\n      const button = [...document.querySelectorAll('button,[role=\"menuitem\"]')]\n        .find((node) => node.textContent?.trim() === 'Describe' && node.getBoundingClientRect().width > 0);\n      if (!button) return false;\n      button.click();\n      return true;\n    });\n    if (!clicked) throw new CommandExecutionError('Midjourney did not expose a Describe action for the uploaded image');\n\n    const deadline = Date.now() + timeout * 1000;\n    let prompts = [];\n    while (Date.now() < deadline) {\n      const groups = await page.evaluate(() => {\n        const visible = (node) => {\n          const rect = node.getBoundingClientRect();\n          return rect.width > 0 && rect.height > 0;\n        };\n        const found = [];\n        const markers = [...document.querySelectorAll('div')]\n          .filter((node) => node.children.length === 0 && node.textContent?.trim() === 'Describe' && visible(node));\n        for (const marker of markers) {\n          let root = marker.parentElement;\n          for (let depth = 0; depth < 16 && root; depth += 1, root = root.parentElement) {\n            const rows = [...root.querySelectorAll('p')]\n              .filter(visible)\n              .map((node) => node.textContent?.trim().replace(/\\s+/g, ' ') || '')","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/describe.js#L71-L107","documentation":"This CommandExecutionError is thrown when the browser automation could not find a visible, clickable 'Describe' button or menu item on the Midjourney web UI after uploading an image. The library clicks 'Describe' via page.evaluate by scanning all button/[role=menuitem] nodes for exact text 'Describe' with nonzero width; if none is found, the Describe workflow cannot proceed and this error aborts the command.","triggerScenarios":"After uploading an image, document.querySelectorAll('button,[role=menuitem]') contains no node whose trimmed textContent is exactly 'Describe' with getBoundingClientRect().width > 0 — e.g. the upload silently failed, the Describe menu did not render, the UI labels changed, or the element is hidden.","commonSituations":"Midjourney UI redesign renaming or moving the Describe action; the image upload failing or being rejected (wrong file type/size) so no Describe option appears; the page not fully loaded or not logged in; popup/overlay blocking the menu from being visible; running in a headless viewport where the element is collapsed.","solutions":["Re-run the command after confirming the image upload completed and the Describe option is visible in the browser session","Re-authenticate/refresh the Midjourney cookie session and retry, since an expired session can render the UI without actions","Check whether Midjourney renamed the Describe action after a UI update; update the selector/text match in clis/midjourney/describe.js if needed","Verify the uploaded file is a supported image type and size, then retry","Increase the wait before searching for the button so the page/menu finishes rendering"],"exampleFix":"// before\nconst button = [...document.querySelectorAll('button,[role=\"menuitem\"]')]\n  .find((node) => node.textContent?.trim() === 'Describe' && node.getBoundingClientRect().width > 0);\n// after (tolerate partial labels)\nconst button = [...document.querySelectorAll('button,[role=\"menuitem\"]')]\n  .find((node) => /describe/i.test(node.textContent || '') && node.getBoundingClientRect().width > 0);","handlingStrategy":"try-catch","validationCode":"// Verify the Describe action exists before invoking the command\nconst hasDescribe = await page.evaluate(() =>\n  [...document.querySelectorAll('button,[role=\"menuitem\"]')].some(\n    (n) => n.textContent?.trim() === 'Describe' && n.getBoundingClientRect().width > 0,\n  ),\n);\nif (!hasDescribe) throw new Error('Describe action not visible; check upload/session before running.');","typeGuard":"function isDescribeAction(node) {\n  return (\n    node instanceof HTMLElement &&\n    node.textContent?.trim() === 'Describe' &&\n    node.getBoundingClientRect().width > 0\n  );\n}","tryCatchPattern":"try {\n  await run('midjourney', 'describe', imagePath);\n} catch (err) {\n  if (err.message.includes('did not expose a Describe action')) {\n    // re-authenticate, confirm upload rendered, and retry once\n  } else throw err;\n}","preventionTips":["Confirm the image upload finished and is visible before invoking describe","Keep the Midjourney cookie/session fresh so the full UI renders","Watch for Midjourney UI changes that rename or move the Describe action","Retry after a short delay if the page was still loading when the command ran"],"tags":["browser-automation","ui-selector","midjourney"],"backgroundTag":"ui-element-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}