jackwener/OpenCLI · error · CommandExecutionError
${label} returned an unexpected payload shape; expected an a
Error message
${label} returned an unexpected payload shape; expected an array of result rows. What it means
requireRows unwraps a browser-command result (peeling off a {session, data} envelope via unwrapBrowserResult) and throws a CommandExecutionError if what remains is not an array of result rows. This indicates the browser step returned something structurally different from what the command's formatter expects — usually page-structure drift or a wrapped/failed payload. It's a shape contract, not a content check (an empty array passes).
Source
Thrown at clis/_shared/search-adapter.js:42
const raw = value ?? defaultValue;
const parsed = typeof raw === 'number' ? raw : Number(raw);
if (!Number.isInteger(parsed) || parsed < 0) {
throw new ArgumentError(`${label} must be a non-negative integer, got ${JSON.stringify(value)}`);
}
return parsed;
}
export function unwrapBrowserResult(value) {
if (value && typeof value === 'object' && !Array.isArray(value) && 'session' in value && 'data' in value) {
return value.data;
}
return value;
}
export function requireRows(value, label) {
const rows = unwrapBrowserResult(value);
if (!Array.isArray(rows)) {
throw new CommandExecutionError(`${label} returned an unexpected payload shape; expected an array of result rows.`);
}
return rows;
}
export function toHttpsUrl(value, baseUrl) {
const raw = String(value ?? '').trim();
if (!raw) return '';
try {
const url = new URL(raw, baseUrl);
if (url.protocol !== 'http:' && url.protocol !== 'https:') return '';
return url.href;
} catch {
return '';
}
}
export function emptySearchResults(site, query) {
return new EmptyResultError(`${site} search`, `No ${site} results matched "${query}".`);View on GitHub (pinned to 49907e53dc)
Solutions
- Log/print the raw browser payload before requireRows to inspect the actual shape.
- If a {session,data} envelope is expected, confirm unwrapBrowserResult is being applied and that data is the array.
- Update the extraction/selector logic to return an array, or update the command for the site's new DOM.
- Pin or upgrade the CLI/adapter versions so payload shape and parser agree.
Example fix
// before (payload shape drifted)
const rows = await runBrowserStep('search', () => page.evaluate(js)); // returns {items:[...]}
requireRows(rows, 'search');
// after
const payload = await runBrowserStep('search', () => page.evaluate(js));
requireRows(Array.isArray(payload) ? payload : payload.items, 'search'); Defensive patterns
Strategy: type-guard
Type guard
function isRows(value) {
const v = value && typeof value === 'object' && !Array.isArray(value) && 'session' in value && 'data' in value
? value.data
: value;
return Array.isArray(v);
} Try / catch
try {
const rows = requireRows(payload, 'search');
} catch (e) {
if (e instanceof CommandExecutionError && /unexpected payload shape/.test(e.message)) {
console.error('Payload:', JSON.stringify(payload).slice(0, 500));
} else throw e;
} Prevention
- Log raw browser payloads when adding new extraction scripts
- Assert result shape right after each browser step
- Keep adapter/parser versions in sync with payload producers
- Treat {session,data} envelopes explicitly before consuming results
When it happens
Trigger: The page.evaluate/browser step resolves to a non-array: an object without being unwrapped, null/undefined, or a {session,data} envelope whose data is not an array.
Common situations: The site changed its markup so the injected extraction script returns an error object instead of rows; an intermediate wrapper/adapter changed its return shape after a version upgrade; a proxy or login wall returned an HTML page parsed into an object.
Related errors
- AIbase daily page returned an unreadable payload
- AIbase daily selector drift: ${reason}
- aibase news
- Unexpected SMZDM search extraction payload shape; expected a
- Not a git repository
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/9137554d7cd7092e.
Report an issue: GitHub.