jackwener/OpenCLI · error · CommandExecutionError

Malformed ${draftType} draft payload

Error message

Malformed ${draftType} draft payload

What it means

readDraftEntries validates that the in-page script's payload contains an entries array. If payload.ok was true but payload.entries is missing or not an Array, it throws CommandExecutionError with 'Malformed <draftType> draft payload'. This is a contract check against the browser-side result.

Source

Thrown at clis/xiaohongshu/draft-utils.js:132

        return {
          ok: true,
          entries: allRows.map((row, index) => ({ key: allKeys[index] ?? index, row })),
        };
      } catch (error) {
        return { ok: false, error: String(error && error.message || error) };
      }
    })()
  `;
}

export async function readDraftEntries(page, draftType) {
    const storeName = STORE_NAME_MAP[draftType];
    const payload = unwrapBrowserResult(await page.evaluate(draftReadScript(storeName)));
    if (!payload?.ok) {
        throw new CommandExecutionError(payload?.error || `Failed to read ${draftType} drafts`);
    }
    if (!Array.isArray(payload.entries)) {
        throw new CommandExecutionError(`Malformed ${draftType} draft payload`);
    }
    return payload.entries;
}

export function draftNotFound(id, draftType, command) {
    return new EmptyResultError(
        command,
        `Draft ${id} was not found in ${draftType} drafts. Run opencli xiaohongshu drafts --type ${draftType} to list current ids.`,
    );
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the actual payload shape by evaluating draftReadScript(storeName) in DevTools on the drafts page
  2. Update draftReadScript to always return { ok: true, entries: [...] }
  3. Confirm the logged-in account actually has the draft store present; empty stores should still return []
  4. Re-check STORE_NAME_MAP mapping matches the current site store names

Example fix

// before (in-page script)
return { ok: true, entries: store.all() };
// after (guarantee array)
const all = store.all ? store.all() : [];
return { ok: true, entries: Array.isArray(all) ? all : [all] };
Defensive patterns

Strategy: type-guard

Validate before calling

const payload = unwrapBrowserResult(await page.evaluate(draftReadScript(storeName)));
if (!payload?.ok || !Array.isArray(payload.entries)) throw new Error('draft payload malformed');

Type guard

function hasEntries(p) { return !!p && typeof p === 'object' && p.ok === true && Array.isArray(p.entries); }

Try / catch

try {
  const entries = await readDraftEntries(page, draftType);
} catch (err) {
  if (err instanceof CommandExecutionError && err.message.includes('Malformed')) {
    // dump raw payload for debugging, update draftReadScript to normalize to array
  } else throw err;
}

Prevention

When it happens

Trigger: page.evaluate(draftReadScript(storeName)) returns { ok: true } but entries is undefined, null, or a non-array (e.g. an object or single item) because the in-page script returned the wrong shape.

Common situations: Site script changes altering the draft store schema, a partial/empty store returning undefined, or a hand-edited draftReadScript that returns the store value directly instead of wrapping it in an entries array.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/747cd8242deaf23e. Report an issue: GitHub.