jackwener/OpenCLI · error · ArgumentError

must be a Kimi chat id or https://www.kimi.com/chat/

Error message

must be a Kimi chat id or https://www.kimi.com/chat/<id> URL

What it means

maybeNavigateConv validates the `conv` argument by running it through parseChatId, which accepts either a bare Kimi chat id or a https://www.kimi.com/chat/<id> URL. If parsing fails it throws ArgumentError, since the command cannot know which conversation to open. Note the library additionally appends ?chat_enter_method=history because Kimi's React app only fetches messages for valid entry methods.

Solutions

  1. Copy the id directly from the kimi history listing command output (ChatId column) instead of typing it
  2. Use the full https://www.kimi.com/chat/<id> URL copied from the browser address bar
  3. Trim whitespace and remove trailing slashes/query params that break parsing
  4. Extend parseChatId if your Kimi URLs use a different path pattern

Example fix

// before
await kimiChat({ conv: 'kimi chat abc123' }); // ArgumentError
// after
const conv = 'https://www.kimi.com/chat/abc123'; // or bare 'abc123'
if (!parseChatId(conv)) throw new Error('bad conv: ' + conv);
await kimiChat({ conv });
Defensive patterns

Strategy: validation

Validate before calling

function isKimiConvArg(arg) {
  if (typeof arg !== 'string') return false;
  if (/^[A-Za-z0-9_-]+$/.test(arg)) return true; // bare id
  return /^https:\/\/www\.kimi\.com\/chat\/[A-Za-z0-9_-]+\/?$/.test(arg);
}
if (!isKimiConvArg(conv)) throw new Error('conv must be a Kimi chat id or kimi.com/chat/<id> URL');

Type guard

function parseKimiChatIdSafe(arg) {
  if (typeof arg !== 'string' || !arg.trim()) return null;
  const m = arg.match(/kimi\.com\/chat\/([A-Za-z0-9_-]+)/) || arg.match(/^([A-Za-z0-9_-]+)$/);
  return m ? m[1] : null;
}

Try / catch

try {
  await kimiChat({ conv });
} catch (e) {
  if (e instanceof ArgumentError && /Kimi chat id/.test(e.message)) {
    const fixed = conv?.match(/([A-Za-z0-9_-]{6,})/)?.[1];
    if (fixed) return kimiChat({ conv: fixed });
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing conv as a typo'd id, an empty/whitespace string, a different site's URL (e.g. chatgpt.com/chat/...), a URL missing /chat/ prefix, or a full URL with extra path segments that parseChatId cannot extract.

Common situations: Copying a Kimi share URL in a different format than /chat/<id>; pasting a conversation id from another AI site; shell quoting stripping characters from the id; passing a title instead of an id from a previous listing.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at clis/kimi/chat.js:29

    KIMI_DOMAIN,
    KIMI_URL,
    IS_VISIBLE_JS,
    ensureOnKimi,
    parseChatId,
    clickBySvgNameScript,
} from './_utils.js';

const CHAT_COLUMNS = ['Field', 'Value', 'Status', 'Url', 'Index', 'Title', 'ChatId', 'Role', 'Text', 'Length', 'ClipboardClicked', 'Reaction', 'WaitedSeconds', 'ReplyPreview'];

// Helper: if --conv passed, navigate to that chat URL and wait briefly
// for messages to mount. Otherwise just ensure we're on kimi.com.
async function maybeNavigateConv(page, convArg) {
    if (!convArg) {
        await ensureOnKimi(page);
        return;
    }
    const id = parseChatId(convArg);
    if (!id) throw new ArgumentError('conv', 'must be a Kimi chat id or https://www.kimi.com/chat/<id> URL');
    // CRITICAL: Kimi's React app only triggers the messages fetch when the
    // URL has ?chat_enter_method=history (or other valid entry methods).
    // Plain /chat/<id> loads the conversation TITLE but leaves the message
    // container empty (clientHeight=0). Append the query param.
    await page.goto(`${KIMI_URL}chat/${id}?chat_enter_method=history`);
    await page.wait(2);
    // Poll up to 15s for .chat-content-list items to appear.
    for (let i = 0; i < 15; i++) {
        const ok = await page.evaluate(`(() => {
      const list = document.querySelector('.chat-content-list') || document.querySelector('.message-list');
      if (!list) return false;
      // Check for actual chat-content-item rows (the new container) OR any direct children (older container)
      return list.querySelectorAll('.chat-content-item, .segment').length > 0 || list.children.length > 0;
    })()`);
        if (ok) return;
        await page.wait(1);
    }
}

View on GitHub (pinned to 49907e53dc)