{"record":{"id":"71429f82baec5340","repo":"jackwener/OpenCLI","slug":"xianyu-search-min-price-cannot-be-greater-than-max","errorCode":null,"errorMessage":"xianyu search min-price cannot be greater than max-price","messagePattern":"xianyu search min-price cannot be greater than max-price","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xianyu/search.js","lineNumber":214,"sourceCode":"    strategy: Strategy.COOKIE,\n    navigateBefore: false,\n    browser: true,\n    args: [\n        { name: 'query', required: true, positional: true, help: '搜索关键词' },\n        { name: 'limit', type: 'int', default: 20, help: `返回结果数（最多 ${MAX_LIMIT}，自动翻页）` },\n        { name: 'min-price', type: 'float', help: '最低价格（元），服务端筛选' },\n        { name: 'max-price', type: 'float', help: '最高价格（元），服务端筛选' },\n        { name: 'province', type: 'string', help: '省份名（如 广东），服务端按地区筛选' },\n        { name: 'city', type: 'string', help: '城市名（如 深圳 / 湛江），可单独使用，服务端按地区筛选' },\n    ],\n    columns: ['item_id', 'rank', 'title', 'price', 'condition', 'brand', 'location', 'badge', 'want', 'url'],\n    func: async (page, kwargs) => {\n        const query = String(kwargs.query || '').trim();\n        const limit = normalizeLimit(kwargs.limit);\n        const minPrice = parsePriceArg(kwargs['min-price'], 'min-price');\n        const maxPrice = parsePriceArg(kwargs['max-price'], 'max-price');\n        if (minPrice != null && maxPrice != null && minPrice > maxPrice) {\n            throw new ArgumentError('xianyu search min-price cannot be greater than max-price', `Received --min-price ${minPrice} and --max-price ${maxPrice}`);\n        }\n        const province = String(kwargs.province || '').trim();\n        const city = String(kwargs.city || '').trim();\n        const searchFilter = buildSearchFilter(minPrice, maxPrice);\n        const extraFilterValue = buildExtraFilterValue(province, city);\n        const fromFilter = Boolean(searchFilter) || extraFilterValue !== '{}';\n        await page.goto(buildSearchUrl(query));\n        await page.wait(2);\n        const result = await page.evaluate(buildSearchEvaluate({ keyword: query, searchFilter, extraFilterValue, fromFilter, maxItems: limit }));\n        if (result?.error === 'auth-required') {\n            throw new AuthRequiredError('www.goofish.com', 'Xianyu search requires a logged-in browser session');\n        }\n        if (result?.error === 'blocked') {\n            throw new CommandExecutionError('Xianyu returned a verification page or blocked the current browser session');\n        }\n        if (result?.error === 'mtop-not-ready') {\n            throw selectorError('window.lib.mtop', '闲鱼页面未完成初始化，无法调用搜索接口');\n        }","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xianyu/search.js#L196-L232","documentation":"After both price bounds are parsed, the CLI checks that min-price <= max-price. A reversed range cannot be sent to Goofish's server-side price filter (priceRange:<min>,<max>;), so an ArgumentError is thrown listing both received values.","triggerScenarios":"Running `xianyu search` with --min-price greater than --max-price, e.g. --min-price 5000 --max-price 1000.","commonSituations":"Swapping flag order mentally ('min 5000 max 1000' when intending a budget of 1000), or computing the bounds from variables where the values were reversed.","solutions":["Swap the values: --min-price 1000 --max-price 5000","Omit one bound to do a single-sided range (e.g. only --max-price 5000)","Verify the order of variables producing the two flags in scripts"],"exampleFix":"// before\nxianyu search 'mechanical keyboard' --min-price 800 --max-price 300\n// after\nxianyu search 'mechanical keyboard' --min-price 300 --max-price 800","handlingStrategy":"validation","validationCode":"const min = Number(minPrice), max = Number(maxPrice);\nif (Number.isFinite(min) && Number.isFinite(max) && min > max) throw new Error(`min-price ${min} > max-price ${max}`);","typeGuard":"const isSaneRange = (min, max) => min == null || max == null || Number(min) <= Number(max);","tryCatchPattern":"try { await xianyuSearch({ 'min-price': min, 'max-price': max }); } catch (e) { if (e instanceof ArgumentError && /greater than max-price/.test(e.message)) { [min, max] = [max, min]; /* swap and retry */ } else throw e; }","preventionTips":["Sort min/max programmatically before passing them as flags","When deriving bounds from variables, assert order with an invariant check","Omit one bound when you only need a single-sided range"],"tags":["argument-validation","cli","price-filter"],"backgroundTag":"invalid-argument-range","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}