jackwener/OpenCLI · error · CommandExecutionError

expected files array, got ${typeof files} (contract drift?)

Error message

expected files array, got ${typeof files} (contract drift?)

What it means

After evaluating the in-page fetch snippet, `channel-files` normalizes the result to a `files` array and throws CommandExecutionError if the result is neither an array nor an object with a `files` array. The '(contract drift?)' note flags that Slock's response shape changed from what the CLI expects.

Source

Thrown at clis/slock/channel-files.js:40

  ],
  columns: ['id', 'filename', 'mimeType', 'sizeBytes', 'messageId', 'createdAt'],
  func: async (page, kwargs) => {
    const channel = String(kwargs.channel ?? '').trim();
    if (!channel) throw new ArgumentError('channel required');
    const limit = parsePositiveInteger(kwargs.limit, '--limit', { defaultValue: 50 });
    await page.goto(SLOCK_HOME_URL);
    const snippet = buildChannelScopedSnippet({
      channelInput: channel,
      method: 'GET',
      pathSuffix: '/files',
      query: `?limit=${limit}`,
      serverIdOverride: kwargs.server,
    });
    const result = await page.evaluate(`(async () => { ${snippet} })()`);
    const data = dispatchEvaluateResult(result);
    const files = Array.isArray(data) ? data : (data.files || []);
    if (!Array.isArray(files)) {
      throw new CommandExecutionError(`expected files array, got ${typeof files} (contract drift?)`);
    }
    return files.map((f) => ({
      id: f.id ?? '',
      filename: f.filename ?? '',
      mimeType: f.mimeType ?? '',
      sizeBytes: typeof f.sizeBytes === 'number' ? f.sizeBytes : null,
      messageId: f.messageId ?? '',
      createdAt: f.createdAt ?? '',
    }));
  },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-authenticate / refresh the session in the Slock app, then retry
  2. Update the slock CLI package to the latest version matching the current Slock app contract
  3. Log the raw result (rerun with debug output) and inspect the actual response shape
  4. File/check an issue against the CLI if the Slock API changed its files payload

Example fix

// before (in CLI)
const files = Array.isArray(data) ? data : (data.files || []);
// after (defensive)
const files = Array.isArray(data) ? data : (Array.isArray(data.files) ? data.files : Array.isArray(data.data?.files) ? data.data.files : []);
Defensive patterns

Strategy: type-guard

Validate before calling

// cannot be validated pre-call; inspect raw output when debugging
// rerun with debug logging to capture the raw page.evaluate result shape

Type guard

const isFilesPayload = (d) => Array.isArray(d) || (d && typeof d === 'object' && Array.isArray(d.files));

Try / catch

try {
  const files = await slock.channelFiles({ channel: '#ops' });
} catch (e) {
  if (e instanceof CommandExecutionError && /contract drift/.test(e.message)) {
    // refresh session, then upgrade the CLI to match the current Slock contract
    await refreshSession();
    return retryWithUpdatedCli();
  }
  throw e;
}

Prevention

When it happens

Trigger: The evaluated snippet returns an object without a `files` array — e.g. Slock returns `{ data: {...} }`, an error envelope, or an HTML/login page parsed as an object instead of the expected `{ files: [...] }` or plain array.

Common situations: Slock app update changing the files endpoint response shape, session expiry causing the snippet to receive an auth redirect body, or a server returning an error object the CLI's `dispatchEvaluateResult` passes through.

Related errors


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