jackwener/OpenCLI · error · ArgumentError

${e.message}

Error message

${e.message}

What it means

This ArgumentError wraps the message thrown by assertMessageIdShape when the supplied messageId fails shape validation before bookmarking. Message IDs have a strict format the server expects; an invalid one would cause a pointless HTTP round trip, so the CLI rejects it locally, preserving the underlying message via e.message.

Source

Thrown at clis/slock/bookmark-add.js:26

cli({
  site: SLOCK_SITE,
  name: 'bookmark-add',
  access: 'write',
  description: 'Bookmark a message (POST /channels/saved). Requires full messageId UUID.',
  domain: SLOCK_DOMAIN,
  strategy: Strategy.COOKIE,
  browser: true,
  siteSession: 'persistent',
  args: [
    { name: 'messageId', positional: true, required: true, help: 'Full messageId UUID (short ids rejected)' },
    { name: 'server', help: 'Override active server' },
  ],
  columns: ['messageId', 'saved'],
  func: async (page, kwargs) => {
    let id;
    try { id = assertMessageIdShape(String(kwargs.messageId ?? '')); }
    catch (e) { throw new ArgumentError(e.message); }
    await page.goto(SLOCK_HOME_URL);
    const snippet = `
      ${authHeadersFragment({ serverScoped: true, serverIdOverride: kwargs.server })}
      const res = await fetch('${SLOCK_API_BASE}/channels/saved', { method:'POST', credentials:'include', headers, body: JSON.stringify({ messageId: ${JSON.stringify(id)} }) });
      if (!res.ok) return { kind: res.status===401?'auth':'http', status: res.status, where:'/channels/saved' };
      const data = await res.json().catch(() => ({}));
      // F3-a — qatester live dump: response is { ok: true } with NO id field.
      // The bookmark is keyed by messageId on the server side, so there is no
      // separate bookmark id to surface; we report saved=true and echo the
      // message id back.
      return { kind: 'ok', rows: [{ saved: data && data.ok === true, messageId: ${JSON.stringify(id)} }] };
    `;
    const result = await page.evaluate(`(async () => { ${snippet} })()`);
    const rows = dispatchEvaluateResult(result);
    return rows.map((b) => ({ messageId: b.messageId ?? id, saved: b.saved === true }));
  },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-fetch the message (or list bookmarks/messages) and pass the exact messageId value from the API.
  2. Trim whitespace and ensure you're not passing a display name or URL instead of the id.
  3. Validate the format locally with the same rules as assertMessageIdShape before calling.
  4. If ids come from another system, map them to Slock message ids first; cross-system ids are never valid.

Example fix

// before
await bookmarkAdd(page, { messageId: row.title });
// after
await bookmarkAdd(page, { messageId: row.messageId });
Defensive patterns

Strategy: validation

Validate before calling

const messageId = String(kwargs.messageId ?? '').trim();
if (!messageId) throw new Error('messageId required');

Type guard

const hasMessageIdShape = (v) => typeof v === 'string' && v.trim().length > 0 && !v.includes(' ');

Try / catch

try {
  await bookmarkAdd(page, { messageId });
} catch (e) {
  if (e instanceof ArgumentError) {
    console.error(`Invalid messageId "${messageId}": ${e.message}`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling bookmark-add with messageId that is empty, whitespace, or otherwise violates assertMessageIdShape's expected format (e.g. passing a channel name, a UUID of another entity, or a truncated id).

Common situations: Copy-pasting a message reference from rendered output instead of the raw id, an off-by-one field selection in a script, or upstream tooling that changed its id format.

Related errors


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