jackwener/OpenCLI · error · ArgumentError

${e.message}

Error message

${e.message}

What it means

This ArgumentError wraps the underlying message from assertMessageIdShape when the messageId argument to bookmark-remove fails shape validation. As with bookmark-add, the CLI validates locally before issuing the DELETE to /channels/saved/:id so bad ids never reach the server.

Source

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

cli({
  site: SLOCK_SITE,
  name: 'bookmark-remove',
  access: 'write',
  description: 'Remove a bookmark (DELETE /channels/saved/:messageId). 404 is treated as already-removed.',
  domain: SLOCK_DOMAIN,
  strategy: Strategy.COOKIE,
  browser: true,
  siteSession: 'persistent',
  args: [
    { name: 'messageId', positional: true, required: true, help: 'Full messageId UUID' },
    { name: 'server', help: 'Override active server' },
  ],
  columns: ['messageId', 'removed', 'note'],
  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/' + encodeURIComponent(${JSON.stringify(id)}), { method:'DELETE', credentials:'include', headers });
      if (res.status === 404) return { kind: 'http', status: 404, where:'/channels/saved/:id' };
      if (!res.ok) return { kind: res.status===401?'auth':'http', status: res.status, where:'/channels/saved/:id' };
      return { kind: 'ok', rows: [{ removed: true }] };
    `;
    const result = await page.evaluate(`(async () => { ${snippet} })()`);
    if (result && result.kind === 'http' && result.status === 404) {
      return [{ messageId: id, removed: true, note: 'idempotent (already absent)' }];
    }
    const rows = dispatchEvaluateResult(result);
    return rows.map(() => ({ messageId: id, removed: true, note: '' }));
  },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Run bookmark-list to get current valid messageIds and use one of those values.
  2. Check the argument for stray whitespace, quotes, or truncation before invoking.
  3. Validate locally against the expected id shape (mirror assertMessageIdShape) to fail fast in scripts.
  4. If automating, assert every id in the batch matches the format before starting the loop.

Example fix

// before
ids.forEach(id => removeBookmark(page, { messageId: id }));
// after
ids.filter(id => id && id.trim().length > 0).forEach(id => removeBookmark(page, { messageId: id.trim() }));
Defensive patterns

Strategy: validation

Validate before calling

const id = String(messageId ?? '').trim();
if (!id) throw new Error(`bookmark-remove: messageId must be non-empty, got "${messageId}"`);

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Calling bookmark-remove with an empty, whitespace-only, or malformed messageId string — e.g. passing a bookmark row id instead of the message id, or a stale id pasted from output.

Common situations: Scripting bulk removals from a CSV where one column was misaligned, trimming errors that drop characters, or ids copied from a different server's export.

Related errors


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