jackwener/OpenCLI · error · CommandExecutionError
expected array of rows from server, got ${typeof rows} (cont
Error message
expected array of rows from server, got ${typeof rows} (contract drift?) What it means
This CommandExecutionError is thrown when dispatchEvaluateResult returns something that is not an array of bookmark rows. The bookmark-list command's contract says the /channels/saved response must dispatch to an array; anything else means the server response shape changed (contract drift) or the envelope was misparsed.
Source
Thrown at clis/slock/bookmark-list.js:41
columns: ['id', 'messageId', 'content', 'savedAt'],
func: async (page, kwargs) => {
const limit = parsePositiveInteger(kwargs.limit, '--limit', { defaultValue: 50 });
const offset = parseNonNegativeInteger(kwargs.offset, '--offset', { defaultValue: 0 });
await page.goto(SLOCK_HOME_URL);
const snippet = `
${authHeadersFragment({ serverScoped: true, serverIdOverride: kwargs.server })}
const res = await fetch('${SLOCK_API_BASE}/channels/saved?limit=' + encodeURIComponent(${JSON.stringify(limit)}) + '&offset=' + encodeURIComponent(${JSON.stringify(offset)}), { credentials:'include', headers });
if (!res.ok) return { kind: res.status===401?'auth':'http', status: res.status, where:'/channels/saved' };
const data = await res.json();
// F3-b — qatester live dump: shape is { saved: [...], hasMore }.
// Unwrap .saved first; fall back to legacy .bookmarks / .data /
// bare array for forward-compat.
return { kind: 'ok', rows: Array.isArray(data) ? data : (data.saved || data.bookmarks || data.data || []) };
`;
const result = await page.evaluate(`(async () => { ${snippet} })()`);
const rows = dispatchEvaluateResult(result);
if (!Array.isArray(rows)) {
throw new CommandExecutionError(`expected array of rows from server, got ${typeof rows} (contract drift?)`);
}
return rows.map((b) => ({
id: b.id ?? '',
messageId: b.messageId ?? '',
content: b.content ?? b.message?.content ?? '',
savedAt: b.savedAt ?? b.createdAt ?? '',
}));
},
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Log the raw result before dispatch to see the actual response shape from /channels/saved.
- Upgrade or downgrade the CLI so its parsing matches your server's API version.
- If the server renamed the collection field, patch the snippet's fallback chain (add the new key).
- Retry after confirming you're hitting the right server (serverScoped override) — some deployments have divergent shapes.
Defensive patterns
Strategy: type-guard
Type guard
const isRowArray = (v) => Array.isArray(v) && v.every((x) => x != null && typeof x === 'object');
Try / catch
try {
const bookmarks = await bookmarkList(page);
} catch (e) {
if (e instanceof CommandExecutionError && e.message.includes('contract drift')) {
console.error('Response shape changed — check server/CLI version match:', e.message);
} else throw e;
} Prevention
- Keep CLI and server versions aligned.
- Inspect the raw /channels/saved response after any server upgrade.
- Guard downstream .map() calls with Array.isArray checks of your own.
When it happens
Trigger: The server returns an object for the saved-list endpoint that dispatchEvaluateResult maps to a non-array (e.g. a pagination envelope {items, total}), or the snippet's fallback extraction (data.saved || data.bookmarks || data.data || []) misses the new key.
Common situations: A Slock server upgrade introducing a paginated/renamed response, a proxy injecting an HTML error page that parses oddly, or an older server whose field names differ from all the fallbacks.
Related errors
- Flomo API returned a malformed memo entry
- Pinterest request returned malformed API payload
- no signed url returned for attachment ${id}
- unexpected /auth/me result: ${JSON.stringify(r)}
- limit must be a positive integer (1-${max})
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e08a5ec7288b872b.
Report an issue: GitHub.