jackwener/OpenCLI · error · CommandExecutionError
Midjourney job-status endpoint returned a malformed payload
Error message
Midjourney job-status endpoint returned a malformed payload
What it means
fetchJobStatuses POSTs job IDs to Midjourney's /api/job-status endpoint and expects the response JSON to be an array of job rows. When the payload parses but is not an array (e.g. an error object, HTML page, or null), the function treats it as unusable and throws CommandExecutionError. This guards callers from undefined behavior when assuming array access on the result.
Source
Thrown at clis/midjourney/utils.js:343
if (!payload || typeof payload !== 'object' || !Array.isArray(payload.data)) {
throw new CommandExecutionError('Midjourney history endpoint returned a malformed payload');
}
return {
data: payload.data,
cursor: stringOrNull(payload.cursor),
checkpoint: stringOrNull(payload.checkpoint),
};
}
export async function fetchJobStatuses(page, jobIds) {
if (!Array.isArray(jobIds) || !jobIds.length) return [];
const payload = await midjourneyJson(page, '/api/job-status', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { jobIds, _frontend_source: 'opencli_adapter' },
});
if (!Array.isArray(payload)) {
throw new CommandExecutionError('Midjourney job-status endpoint returned a malformed payload');
}
return payload;
}
export async function fetchJobStatus(page, jobId, { allowMissing = false } = {}) {
const payload = await fetchJobStatuses(page, [jobId]);
const job = payload.find((row) => row?.id === jobId) || null;
if (!job && !allowMissing) {
throw new EmptyResultError('midjourney status', `Job ${jobId} was not found in the current account.`);
}
return job;
}
export async function cancelMidjourneyJob(page, jobId) {
let result;
try {
result = unwrapEvaluateResult(await page.evaluate(async (id) => {
const response = await fetch('/api/job-cancel', {View on GitHub (pinned to 49907e53dc)
Solutions
- Log into Midjourney in the driven Chrome session and verify the page can load midjourney.com (most non-array responses are auth/HTML interstitials).
- Call fetchJobStatuses again after a short delay — transient Cloudflare/rate-limit responses often resolve on retry.
- Check whether Midjourney changed the /api/job-status response shape and update the adapter to unwrap the new envelope.
- Inspect the raw response (add logging in midjourneyJson) to confirm what the endpoint actually returned.
Example fix
// before
const payload = await midjourneyJson(page, '/api/job-status', {...});
if (!Array.isArray(payload)) {
throw new CommandExecutionError('Midjourney job-status endpoint returned a malformed payload');
}
// after
let payload = await midjourneyJson(page, '/api/job-status', {...});
if (payload && Array.isArray(payload.jobs)) payload = payload.jobs; // unwrap envelope if API changed
if (!Array.isArray(payload)) {
throw new CommandExecutionError('Midjourney job-status endpoint returned a malformed payload');
} Defensive patterns
Strategy: type-guard
Validate before calling
// before calling: confirm session can reach the API surface
const page = await getPage();
if (!page.url().includes('midjourney.com')) throw new Error('Open a midjourney.com page first');
if (!(await isLoggedInToMidjourney(page))) throw new Error('Log into Midjourney before fetching job statuses'); Type guard
function isJobStatusPayload(v) {
return Array.isArray(v) && v.every((row) => row == null || typeof row === 'object');
}
// usage: only trust the result when isJobStatusPayload(payload) holds Try / catch
try {
const jobs = await fetchJobStatuses(page, jobIds);
} catch (e) {
if (String(e.message).includes('malformed payload')) {
// likely auth/HTML interstitial: refresh session and retry once
await page.reload({ waitUntil: 'networkidle' });
const jobs = await fetchJobStatuses(page, jobIds);
} else throw e;
} Prevention
- Keep the Midjourney Chrome session logged in before running status commands
- Wrap non-array API responses at one boundary (midjourneyJson) and log raw bodies for debugging
- Retry transient payload failures once after a short delay
- Watch for Midjourney API shape changes and unwrap new envelopes defensively
When it happens
Trigger: Calling fetchJobStatus/fetchJobStatuses when the /api/job-status endpoint returns JSON that is not an array: an auth/redirect HTML page parsed as JSON, an error object like {error: ...}, a rate-limit JSON body, or a Midjourney API change that wraps the array in an object.
Common situations: Expired or missing Midjourney session cookies causing an HTML login page response; Midjourney API schema changes; Cloudflare or proxy interstitials returning non-array JSON; hitting the endpoint while logged out or rate limited.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Midjourney subscription endpoint returned a malformed payloa
- Midjourney history endpoint returned a malformed payload
- Midjourney settings panel returned an unexpected shape
- Midjourney settings panel is missing required field(s): ${mi
- ${label} returned an unexpected payload shape; expected an o
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/20bae25aa9d71de1.
Report an issue: GitHub.