{"record":{"id":"5ef564720fe250c5","repo":"jackwener/OpenCLI","slug":"xianyu-reply-requires-both-item-id-and-user-id-or","errorCode":null,"errorMessage":"xianyu reply requires both item_id and user_id, or --rank from xianyu inbox","messagePattern":"xianyu reply requires both item_id and user_id, or --rank from xianyu inbox","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xianyu/reply.js","lineNumber":30,"sourceCode":"    strategy: Strategy.COOKIE,\n    navigateBefore: false,\n    browser: true,\n    args: [\n        { name: 'item_id', positional: true, help: '闲鱼商品 item_id' },\n        { name: 'user_id', positional: true, help: '聊一聊对方的 user_id / peerUserId' },\n        { name: 'text', required: true, help: 'Message text to send' },\n        { name: 'rank', type: 'int', default: 0, help: 'Conversation rank from xianyu inbox; clicks the visible row instead of requiring IDs' },\n    ],\n    columns: ['status', 'peer_name', 'item_title', 'price', 'location', 'message'],\n    func: async (page, kwargs) => {\n        const hasItemId = kwargs.item_id != null && kwargs.item_id !== '';\n        const hasUserId = kwargs.user_id != null && kwargs.user_id !== '';\n        const rank = normalizeRank(kwargs.rank);\n        if (rank > 0 && (hasItemId || hasUserId)) {\n            throw new ArgumentError('xianyu reply accepts either item_id/user_id or --rank, not both');\n        }\n        if (rank === 0 && hasItemId !== hasUserId) {\n            throw new ArgumentError('xianyu reply requires both item_id and user_id, or --rank from xianyu inbox');\n        }\n        if (rank === 0 && !hasItemId && !hasUserId) {\n            throw new ArgumentError('xianyu reply requires item_id/user_id or --rank from xianyu inbox');\n        }\n        const hasIds = hasItemId && hasUserId;\n        const itemId = hasIds ? normalizeNumericId(kwargs.item_id, 'item_id', '1038951278192') : '';\n        const userId = hasIds ? normalizeNumericId(kwargs.user_id, 'user_id', '3650092411') : '';\n        const text = requireText(kwargs.text, 'xianyu reply --text');\n        const url = hasIds ? buildChatUrl(itemId, userId) : '';\n        if (hasIds) {\n            await page.goto(url);\n        } else {\n            if (!page.getCurrentUrl || !/https:\\/\\/www\\.goofish\\.com\\/im\\b/.test(await page.getCurrentUrl())) {\n                await page.goto('https://www.goofish.com/im');\n            }\n        }\n        await page.wait(2);\n        if (rank > 0) {","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xianyu/reply.js#L12-L48","documentation":"Without --rank, reply requires BOTH item_id and user_id to build the chat URL. Providing exactly one of them (hasItemId !== hasUserId) is rejected with this ArgumentError, since a partial pair cannot form a goofish chat URL. Thrown at clis/xianyu/reply.js:29-31.","triggerScenarios":"Invoking `xianyu reply <item_id> --text ...` (user_id missing) or `xianyu reply <item_id> <empty-user_id> ...` — one ID set, the other null/empty, rank === 0.","commonSituations":"Copy-pasting only the item URL and forgetting the peer's user_id; user_id field empty in a database record feeding the CLI; confusing this command's two-positional-args signature with single-ID chat CLIs.","solutions":["Supply both positional IDs: `xianyu reply <item_id> <user_id> --text '...'`.","If only the conversation position is known, use `--rank N` from `xianyu inbox` output instead of a partial ID pair.","Fix the upstream data source so user_id is populated before invoking reply."],"exampleFix":"// before: only item_id\nxianyu reply 1038951278192 --text \"still available?\"\n// after: both IDs (or --rank)\nxianyu reply 1038951278192 3650092411 --text \"still available?\"","handlingStrategy":"validation","validationCode":"const hasOne = (v) => v != null && v !== '';\nconst a = hasOne(kwargs.item_id), b = hasOne(kwargs.user_id);\nif (a !== b) throw new Error('xianyu reply needs BOTH item_id and user_id (or use --rank).');","typeGuard":null,"tryCatchPattern":"try {\n  await reply(kwargs);\n} catch (e) {\n  if (String(e.message).includes('requires both item_id and user_id')) {\n    if (!hasOne(kwargs.user_id)) throw new Error('Lookup user_id from the inbox before replying.');\n    if (!hasOne(kwargs.item_id)) throw new Error('Lookup item_id from the conversation before replying.');\n  }\n  throw e;\n}","preventionTips":["Treat item_id and user_id as an inseparable pair when not using --rank.","Backfill user_id in upstream data before automating replies.","Fall back to `xianyu inbox` + --rank when the full ID pair is unavailable."],"tags":["argument-validation","cli-usage","xianyu"],"backgroundTag":"missing-required-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}