{"record":{"id":"6a2be7a9d3665cdb","repo":"jackwener/OpenCLI","slug":"xianyu-category-selection-failed-categoryresult","errorCode":null,"errorMessage":"Xianyu category selection failed: ${categoryResult?.reason || 'unknown-reason'}","messagePattern":"Xianyu category selection failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xianyu/publish.js","lineNumber":405,"sourceCode":"    func: async (page, kwargs) => {\n        const data = normalizePublishArgs(kwargs);\n        // 1. 导航到发布页\n        await page.goto(buildPublishUrl());\n        await page.wait(3);\n\n        // 2. 检查登录状态\n        const initState = await page.evaluate(buildExtractPageStateEvaluate());\n        if (initState?.requiresAuth) {\n            throw new AuthRequiredError('www.goofish.com', '发布闲鱼需要先登录，请在 Chrome 中打开 goofish.com 并完成登录');\n        }\n        if (!initState?.hasPublishForm) {\n            throw new CommandExecutionError('Xianyu publish form was not detected', 'Confirm goofish.com is logged in and the publish page finished loading.');\n        }\n\n        // 3. 选择分类（先于其他字段，因为分类可能影响表单结构）\n        const categoryResult = await page.evaluate(buildSelectCategoryEvaluate(data.category));\n        if (!categoryResult?.ok) {\n            throw new CommandExecutionError(`Xianyu category selection failed: ${categoryResult?.reason || 'unknown-reason'}`);\n        }\n        await page.wait(1.5);\n\n        // 4. 填充表单\n        const fillResult = await page.evaluate(buildFillFormEvaluate(data));\n        if (!fillResult?.ok) {\n            const missing = Array.isArray(fillResult?.missing) ? fillResult.missing.join(', ') : 'unknown';\n            throw new CommandExecutionError(`Xianyu publish form fill failed; missing fields: ${missing}`);\n        }\n        await page.wait(1);\n\n        // 5. 上传图片（如果有）\n        if (data.images.length > 0) {\n            if (!page.setFileInput) {\n                throw new CommandExecutionError('Xianyu publish requires Browser Bridge file upload support', 'Use a browser mode that supports setFileInput.');\n            }\n            const fileInput = await page.evaluate(buildFindFileInputSelectorEvaluate());\n            if (!fileInput?.ok) {","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xianyu/publish.js#L387-L423","documentation":"The publish flow calls buildSelectCategoryEvaluate(data.category) in the page; if the result is falsy or lacks ok, it throws CommandExecutionError including the in-page reason, or 'unknown-reason' when none is provided. It means the category could not be selected on the goofish publish form.","triggerScenarios":"data.category matching no available option in the form's category tree, or the category dialog/menu failing to open or render (categoryResult.ok false/undefined with a reason string).","commonSituations":"Category name at the wrong tree level or with a typo, goofish renaming categories, the category menu rendering slower than the evaluate runs, or form layout changes breaking in-page selection logic.","solutions":["Use an exact category name supported by the form (read the in-page reason for specifics)","Increase the wait before category selection so the dialog/menu is rendered","Omit an unmatchable custom category and fall back to the site default","Update buildSelectCategoryEvaluate selectors if goofish changed its DOM"],"exampleFix":"// before\nawait publish({ category: '数码/二手手机/苹果 iPhone 99', ... }); // nonexistent leaf\n// after\nawait publish({ category: '手机', ... }); // valid, supported category","handlingStrategy":"validation","validationCode":"const SUPPORTED_CATEGORIES = ['手机', '数码', '服装', '家居']; // from your catalog\nif (!SUPPORTED_CATEGORIES.includes(data.category)) {\n  throw new Error(`Unsupported category: ${data.category}`);\n}","typeGuard":"function isValidCategory(c) {\n  return typeof c === 'string' && c.trim().length > 0;\n}","tryCatchPattern":"try {\n  await publish(data);\n} catch (e) {\n  if (e instanceof CommandExecutionError && /category selection failed/.test(e.message)) {\n    const reason = e.message.match(/failed: (.+)$/)?.[1];\n    console.error(`Category \"${data.category}\" rejected (${reason}); retrying without category`);\n    await publish({ ...data, category: undefined });\n  } else throw e;\n}","preventionTips":["Maintain a list of exact category names from the live site","Log categoryResult.reason to distinguish 'not found' vs 'dialog did not open'","Increase the pre-selection wait so the category menu is mounted"],"tags":["browser-automation","dom","category"],"backgroundTag":"dom-selector-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}