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

After evaluating the server-scoped fetch snippet, `channel-list` expects `dispatchEvaluateResult` to return an array of channel rows and throws CommandExecutionError otherwise. '(contract drift?)' signals the server/CLI response contract changed — e.g. the endpoint now returns `{ channels: [...] }` or an error object.

Source

Thrown at clis/slock/channel-list.js:32

  strategy: Strategy.COOKIE,
  browser: true,
  siteSession: 'persistent',
  args: [
    { name: 'server', help: 'Override active server (slug or id) for this call' },
  ],
  columns: ['id', 'name', 'topic'],
  func: async (page, kwargs) => {
    await page.goto(SLOCK_HOME_URL);
    const snippet = buildFetchSnippet({
      method: 'GET',
      path: '/channels/',
      serverScoped: true,
      serverIdOverride: kwargs.server,
    });
    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((c) => ({
      id: c.id ?? '',
      name: c.name ?? c.slug ?? '',
      topic: c.topic ?? '',
    }));
  },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-authenticate in the Slock app / refresh session and retry
  2. Update the slock CLI to the version matching the current Slock app
  3. Inspect the raw evaluate result with debug output to see the actual shape
  4. Patch normalization locally (e.g. unwrap `data.channels`) and report upstream

Example fix

// before (in CLI)
const rows = dispatchEvaluateResult(result);
if (!Array.isArray(rows)) throw new CommandExecutionError(...);
// after (defensive unwrap)
let rows = dispatchEvaluateResult(result);
if (!Array.isArray(rows) && Array.isArray(rows?.channels)) rows = rows.channels;
Defensive patterns

Strategy: type-guard

Validate before calling

// cannot be validated pre-call; capture the raw response when debugging
// add debug logging around page.evaluate to see what the server returned

Type guard

const isRowsPayload = (d) => Array.isArray(d) || (d && typeof d === 'object' && Array.isArray(d.channels));

Try / catch

try {
  const channels = await slock.channelList();
} catch (e) {
  if (e instanceof CommandExecutionError && /contract drift/.test(e.message)) {
    await refreshSession();
    return retryWithUpdatedCli();
  }
  throw e;
}

Prevention

When it happens

Trigger: The evaluated snippet returns a non-array — Slock returns a wrapped object (`{ channels: [...] }`, `{ data: [...] }`), an auth/login HTML payload, or an error envelope the CLI doesn't unwrap.

Common situations: Slock app update altering the channels endpoint shape, expired session causing a redirect/error body, or using an old CLI against a newer Slock instance.

Related errors


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