jackwener/OpenCLI · error · CommandExecutionError

expected array of rows from server, got ${typeof list} (cont

Error message

expected array of rows from server, got ${typeof list} (contract drift?)

What it means

message-read.js expects the server evaluate result (after dispatchEvaluateResult) to be an array of thread/message rows so it can map each via mapRow. If the normalized result is not an array, it throws this CommandExecutionError indicating a server/CLI contract drift.

Source

Thrown at clis/slock/message-read.js:83

    const params = {
      isUuid, channel, after, before, limit, noThreads, override,
      parentTarget: tt?.parentTarget ?? '',
      parentMsgId: tt?.parentMsgId ?? '',
      isThread: !!tt,
    };
    const snippet = buildReadSnippet(params);
    const result = await page.evaluate(`(async () => { ${snippet} })()`);

    if (result?.kind === 'no-thread') {
      return [{
        id: '', seq: null, createdAt: '', senderName: '',
        content: `(${result.parent} — no thread yet, 0 replies.)`,
        threadChannelId: null, replyCount: 0, unreadCount: 0, lastReplyAt: null,
      }];
    }
    const list = dispatchEvaluateResult(result);
    if (!Array.isArray(list)) {
      throw new CommandExecutionError(`expected array of rows from server, got ${typeof list} (contract drift?)`);
    }
    const threadsMap = result.meta?.threadsMap ?? {};
    const threadsDegraded = result.meta?.threadsDegraded === true;
    const mapArg = threadsDegraded ? null : threadsMap;
    const rows = list.map((m) => mapRow(m, mapArg));
    if (threadsDegraded) {
      rows.unshift({
        id: '', seq: null, createdAt: '', senderName: '',
        content: '(threads-enrichment unavailable — replyCount/threadChannelId set to null. Retry to get reply counts.)',
        threadChannelId: null, replyCount: null, unreadCount: null, lastReplyAt: null,
      });
    }
    return rows;
  },
});

function buildReadSnippet(p) {
  const target = JSON.stringify(p.channel.replace(/^#/, '').toLowerCase());

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Update the CLI to the server's current response contract (check where the array is now nested).
  2. Log the raw `result`/`list` before the throw to inspect the actual shape.
  3. Confirm active server and auth are valid so the real payload is returned.
  4. Check release notes for breaking API changes to the messages endpoint.

Example fix

// before
const list = dispatchEvaluateResult(result);
// after
const raw = dispatchEvaluateResult(result);
const list = Array.isArray(raw) ? raw : (raw.messages || raw.rows || []);
Defensive patterns

Strategy: type-guard

Validate before calling

const list = dispatchEvaluateResult(result);
if (!Array.isArray(list)) throw new Error(`unexpected message-read payload: ${JSON.stringify(list).slice(0,200)}`);

Type guard

function isRowArray(v) { return Array.isArray(v); }

Try / catch

try { const rows = await readMessages(page, kwargs); } catch (e) { if (String(e.message).includes('expected array of rows from server')) { console.error('Response contract drift — inspect raw result and update CLI'); } else throw e; }

Prevention

When it happens

Trigger: Running message-read when the endpoint returns an object envelope (e.g. {messages: [...]}) or an error object instead of a bare array; a degraded threads enrichment path returning unexpected shape.

Common situations: Server upgrade changed the response shape; auth failure returned an error payload that leaked through dispatchEvaluateResult; middlewares/proxies wrapping the array.

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.

Related errors


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