jackwener/OpenCLI · error · CommandExecutionError
Slock message-send succeeded without returning a message id;
Error message
Slock message-send succeeded without returning a message id; refusing to report a sent row.
What it means
message-send drives the Slock web app in a browser page and POSTs to /messages via an in-page fetch snippet. The server returned 200 and the snippet reported kind:'ok', but the JSON body contained neither an `id` nor a `messageId` field. The CLI deliberately refuses to fabricate a 'sent' output row without a real message id, because downstream commands (reaction-add, message-read, bookmark) require it.
Source
Thrown at clis/slock/message-send.js:57
catch (e) { throw new ArgumentError(e.message); }
const extra = { asTask, attachmentIds };
if (kwargs['dry-run']) {
return [{
target, channelId: '(not resolved in dry-run)', content,
result: asTask ? 'dry-run (asTask)' : 'dry-run', messageId: null,
}];
}
await page.goto(SLOCK_HOME_URL);
const snippet = buildSendSnippet(target, content, cls, kwargs.server, extra);
const result = await page.evaluate(`(async () => { ${snippet} })()`);
const rows = dispatchEvaluateResult(result);
const r = rows[0] ?? {};
const messageId = r.id ?? r.messageId;
if (!messageId) {
throw new CommandExecutionError('Slock message-send succeeded without returning a message id; refusing to report a sent row.');
}
return [{
target,
channelId: r.channelId ?? '',
content,
result: 'sent',
messageId,
}];
},
});
function buildSendSnippet(target, content, cls, serverOverride, extra = {}) {
// R1 — raw override; authHeadersFragment owns the UUID-vs-slug resolution.
const override = serverOverride ?? null;
const contentJson = JSON.stringify(content);
const extraParts = [];
if (extra.asTask) extraParts.push('asTask: true');
if (Array.isArray(extra.attachmentIds) && extra.attachmentIds.length) {View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the actual /messages response body (browser devtools Network tab, or log `m` in the snippet at clis/slock/message-send.js:84-86) to see where the id now lives
- Update buildSendSnippet's parsing (`m.id ?? m.messageId`) to unwrap the new response shape, e.g. `m.message?.id`
- Retry without --as-task to confirm plain message sends still return a top-level id
- If targeting a self-hosted server, align it with the upstream Slock API response format for POST /messages
Example fix
// before (clis/slock/message-send.js, in-page snippet) const m = await mres.json(); const messageId = m.id ?? m.messageId; // after const m = await mres.json(); const inner = m.message ?? m.data ?? m; const messageId = inner.id ?? inner.messageId ?? m.id;
Defensive patterns
Strategy: validation
Validate before calling
const res = await page.evaluate(`(async () => { ${snippet} })()`);
const rows = dispatchEvaluateResult(res);
const r = rows[0] ?? {};
const messageId = r.id ?? r.messageId;
if (!messageId) throw new Error(`POST /messages returned 200 without an id: ${JSON.stringify(r).slice(0, 200)}`); Type guard
function hasMessageId(r) {
return r != null && typeof r === 'object' &&
typeof (r.id ?? r.messageId) === 'string' && (r.id ?? r.messageId).length > 0;
} Try / catch
try {
const row = await sendMessage(target, content);
} catch (e) {
if (e.message.includes('without returning a message id')) {
console.error('Send may have succeeded but id was not returned; check server response shape before retrying to avoid duplicate messages.');
} else throw e;
} Prevention
- Pin and monitor the Slock server version; diff POST /messages response schema after upgrades
- Log the raw response body when `m.id ?? m.messageId` is missing
- Avoid relying on --as-task outputs until the task response shape is confirmed
- Do not blindly retry after this error — the message may already have been sent
When it happens
Trigger: The in-page POST to `${SLOCK_API_BASE}/messages` returns HTTP 200 with a JSON body lacking `id`/`messageId` — e.g. the API shape changed and now wraps the message differently (e.g. `{message: {...}}` or `{data: {...}}`), or the endpoint returns an empty object/ack for some content types (e.g. asTask submissions that only return a task reference).
Common situations: Slock server/API version updated and the /messages response schema no longer matches what the snippet parses; sending with --as-task where the backend returns a task object without an `id` field at the top level; a proxy or modified frontend returning 200 with an unexpected body; running against a self-hosted/alternate Slock server whose response format differs.
Related errors
- Unexpected 12306 probe: ${JSON.stringify(probe)}
- Waiting for 12306 tk auth cookie
- amazon.com
- Unexpected Amazon probe: ${JSON.stringify(probe)}
- amazon ${action} navigation lost the current browser target
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/2c453e43ca8b7204.
Report an issue: GitHub.