jackwener/OpenCLI · error · CommandExecutionError
${label} returned a malformed row
Error message
${label} returned a malformed row What it means
normalizeConversationRows validates rows scraped from the Grok conversation list before export. This error is thrown when a row is present in the array but is not a plain object (null, undefined, a primitive, or an Array). The library refuses to guess or coerce such a row, since it cannot read row.id/title/date/url from it.
Source
Thrown at clis/grok/export-utils.js:60
}
const host = parsed.hostname.toLowerCase();
const match = parsed.pathname.match(/^\/c\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/?$/i);
if (parsed.protocol !== 'https:' || (host !== 'grok.com' && !host.endsWith('.grok.com')) || !match) {
throw makeError(`invalid url for conversation ${id}`);
}
if (match[1].toLowerCase() !== id) {
throw makeError(`url id mismatch for conversation ${id}`);
}
return `https://grok.com/c/${id}`;
}
export function normalizeConversationRows(rows, label) {
if (!Array.isArray(rows)) {
throw new CommandExecutionError(`${label} returned malformed rows`, 'Expected rows to be an array.');
}
return rows.map((row, index) => {
if (!row || typeof row !== 'object' || Array.isArray(row)) {
throw new CommandExecutionError(`${label} returned a malformed row`, `Row ${index + 1} is not an object.`);
}
const id = String(row.id || '').trim().toLowerCase();
if (!GROK_CONVERSATION_ID_RE.test(id)) {
throw new CommandExecutionError(`${label} returned a malformed row`, `Row ${index + 1} is missing a valid Grok conversation id.`);
}
return {
id,
title: row.title == null || row.title === '' ? '' : String(row.title),
date: row.date == null || row.date === '' ? '' : String(row.date),
url: normalizeGrokUrl(row.url, id, (reason) => new CommandExecutionError(`${label} returned a malformed row`, reason)),
};
});
}
export function normalizeManifestRows(rows) {
if (!Array.isArray(rows)) {
throw new ArgumentError('manifestPath', 'must point to a JSON array exported by grok/export');
}View on GitHub (pinned to 49907e53dc)
Solutions
- Find the offending index (reported as `Row N is not an object.` in the error detail) and inspect the source array at rows[N-1].
- Fix the upstream scraper/evaluate payload so every element is an object with id, title, date, url fields before calling normalizeConversationRows.
- Filter out non-object entries yourself first: rows = rows.filter(r => r && typeof r === 'object' && !Array.isArray(r)).
- If you only have a manifest JSON file, verify it is an array of objects via `node -e "const d=require('./m.json'); d.forEach((r,i)=>{if(!r||typeof r!=='object'||Array.isArray(r)) throw i+1})"`.
Example fix
// before const rows = await page.evaluate(() => scrapedRows); // scrapedRows may contain nulls const valid = normalizeConversationRows(rows, 'grok/list'); // after const rows = (await page.evaluate(() => scrapedRows)) ?? []; const cleaned = rows.filter((r) => r && typeof r === 'object' && !Array.isArray(r)); const valid = normalizeConversationRows(cleaned, 'grok/list');
Defensive patterns
Strategy: validation
Validate before calling
function isRowArray(rows) {
return Array.isArray(rows) && rows.every((r) => r && typeof r === 'object' && !Array.isArray(r));
}
if (!isRowArray(rows)) throw new Error('conversation rows must be an array of objects'); Type guard
function isConversationRow(row) {
return typeof row === 'object' && row !== null && !Array.isArray(row);
} Try / catch
import { CommandExecutionError } from '@jackwener/opencli/errors';
try {
const rows = normalizeConversationRows(raw, 'grok/list');
} catch (err) {
if (err instanceof CommandExecutionError) {
// err.detail tells you which row index failed
console.error(`Grok export rows malformed: ${err.message} — ${err.detail}`);
} else throw err;
} Prevention
- Filter out null/primitive entries from page-eval results before normalizing.
- Validate the scraper payload shape in a smoke test after any Grok DOM change.
- Never hand-edit exported JSON; always round-trip through grok/export.
When it happens
Trigger: Calling normalizeConversationRows(rows, label) where rows is an array containing at least one element that is null, undefined, a primitive (string/number/boolean), or an array; the error message includes the 1-based row index in `Row ${index + 1} is not an object.`
Common situations: A page-eval extraction script returned null placeholders for conversations that were deleted mid-scrape; a JSON scrape file was hand-edited or truncated; a version change in Grok's DOM scraper produced different shapes; someone passed a top-level JSON object keyed by id instead of an array of rows.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- series_id must be a non-empty value
- Invalid Chess.com username "${value}" Usernames are 3-25 cha
- coingecko derivatives limit must be a positive integer
- limit must be a positive integer
- Search keyword cannot be empty
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/8d713844803e7653.
Report an issue: GitHub.