{"record":{"id":"20f314bbfdedfd26","repo":"jackwener/OpenCLI","slug":"expected-array-of-dm-channels-got-typeof-rows","errorCode":null,"errorMessage":"expected array of DM channels, got ${typeof rows} (contract drift?)","messagePattern":"expected array of DM channels, got (.+?) \\(contract drift\\?\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/slock/dm-list.js","lineNumber":32,"sourceCode":"  strategy: Strategy.COOKIE,\n  browser: true,\n  siteSession: 'persistent',\n  args: [\n    { name: 'server', help: 'Override active server (slug or id)' },\n  ],\n  columns: ['channelId', 'peerName', 'peerId', 'createdAt'],\n  func: async (page, kwargs) => {\n    await page.goto(SLOCK_HOME_URL);\n    const snippet = buildFetchSnippet({\n      method: 'GET',\n      path: '/channels/dm',\n      serverScoped: true,\n      serverIdOverride: kwargs.server,\n    });\n    const result = await page.evaluate(`(async () => { ${snippet} })()`);\n    const rows = dispatchEvaluateResult(result);\n    if (!Array.isArray(rows)) {\n      throw new CommandExecutionError(`expected array of DM channels, got ${typeof rows} (contract drift?)`);\n    }\n    return rows.map((c) => ({\n      channelId: c.id ?? '',\n      peerName: c.peerDisplayName ?? c.peerName ?? c.name ?? '',\n      peerId: c.peerId ?? '',\n      createdAt: c.createdAt ?? '',\n    }));\n  },\n});\n","sourceCodeStart":14,"sourceCodeEnd":42,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/slock/dm-list.js#L14-L42","documentation":"The dm-list command expects the in-page evaluate snippet to return an array of DM channel objects via the dispatchEvaluateResult envelope. This CommandExecutionError signals 'contract drift': the browser-side snippet returned something other than an array, meaning the injected code or the upstream API response shape changed.","triggerScenarios":"The page.evaluate snippet returns an envelope whose rows field is not an array (e.g. API returned an object with an error payload that was misclassified as ok), or the snippet was refactored to return {data: [...]} instead of a bare array.","commonSituations":"Upstream Slack-like API changed its DM list response shape; a partial page load or CSP issue caused the snippet to return undefined; version mismatch between the CLI script and the injected snippet.","solutions":["Update the slock CLI to the latest version so snippet and dispatcher agree on the envelope contract","Log the raw evaluate result (dispatchEvaluateResult input) to inspect the actual shape returned","If the API response shape changed, adjust the snippet to map/normalize the response to an array before returning"],"exampleFix":"// before\nconst rows = dispatchEvaluateResult(result);\n// after\nconst raw = dispatchEvaluateResult(result);\nconst rows = Array.isArray(raw) ? raw : Array.isArray(raw?.data) ? raw.data : (() => { throw new CommandExecutionError('unexpected rows shape'); })();","handlingStrategy":"type-guard","validationCode":"const rows = result && result.kind === 'ok' ? result.rows : null;\nif (!Array.isArray(rows)) throw new Error('dm-list: rows is not an array — check CLI/snippet version');","typeGuard":"const isRowsArray = (r) => r != null && typeof r === 'object' && r.kind === 'ok' && Array.isArray(r.rows);","tryCatchPattern":"try { const rows = dispatchEvaluateResult(result); } catch (e) { if (/contract drift/.test(e.message)) { console.error('CLI/snippet version mismatch — upgrade the slock CLI'); } throw e; }","preventionTips":["Keep the slock CLI and any custom snippets on matching versions","Log raw evaluate results once when integrating","Pin the CLI version in automation and update deliberately"],"tags":["contract-drift","cli","type-error"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}