{"record":{"id":"05fbe461c1261048","repo":"jackwener/OpenCLI","slug":"model-wantset-is-ambiguous-partialmatches","errorCode":null,"errorMessage":"Model \"${wantSet}\" is ambiguous: ${partialMatches.map(item => item.model).join(', ')}","messagePattern":"Model \"(.+?)\" is ambiguous: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/kimi/ui.js","lineNumber":261,"sourceCode":"      const pop = popovers[popovers.length - 1];\n      return Array.from(pop.querySelectorAll('div, li, button, [role=\"option\"], [role=\"menuitem\"]'))\n        .filter(isVisible)\n        .map((el) => (el.innerText || el.textContent || '').trim().replace(/\\\\s+/g, ' '))\n        .filter((t) => t && t.length < 80 && (/K\\\\d|Kimi|Pro\\\\b|Auto|思考/.test(t)))\n        .filter((t, i, arr) => arr.indexOf(t) === i)\n        .slice(0, 20);\n    })()`);\n\n        if (wantSet) {\n            const normalizeModel = (value) => String(value || '').toLowerCase().replace(/[^a-z0-9.\\u4e00-\\u9fa5]+/g, '');\n            const needle = normalizeModel(wantSet);\n            const exactIdx = opts.findIndex((t) => normalizeModel(t) === needle);\n            const partialMatches = exactIdx >= 0 ? [] : opts\n                .map((model, index) => ({ model, index }))\n                .filter((item) => normalizeModel(item.model).includes(needle));\n            if (exactIdx < 0 && partialMatches.length > 1) {\n                try { await page.evaluate(`document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));`); } catch {}\n                throw new ArgumentError('set', `Model \"${wantSet}\" is ambiguous: ${partialMatches.map(item => item.model).join(', ')}`);\n            }\n            const idx = exactIdx >= 0 ? exactIdx : partialMatches[0]?.index ?? -1;\n            if (idx < 0) {\n                try { await page.evaluate(`document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));`); } catch {}\n                throw new ArgumentError('set', `No model matched \"${wantSet}\". Available: ${opts.join(', ')}`);\n            }\n            const clickRes = await page.evaluate(`(() => {\n        ${IS_VISIBLE_JS}\n        const popovers = Array.from(document.querySelectorAll('[class*=\"popover\"i], [class*=\"dropdown\"i], [role=\"menu\"], [role=\"listbox\"]')).filter(isVisible);\n        const pop = popovers[popovers.length - 1];\n        if (!pop) return { ok: false };\n        const items = Array.from(pop.querySelectorAll('div, li, button, [role=\"option\"], [role=\"menuitem\"]'))\n          .filter(isVisible)\n          .filter((el) => { const t = (el.innerText || '').trim(); return t && t.length < 80 && (/K\\\\d|Kimi|Pro\\\\b|Auto|思考/.test(t)); });\n        const target = items[${idx}];\n        if (!target) return { ok: false };\n        target.click();\n        return { ok: true, clicked: (target.innerText || '').trim() };","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/kimi/ui.js#L243-L279","documentation":"When setting the Kimi model, the command normalizes the requested name and matches it against the options detected in the open model dropdown. If there is no exact normalized match but more than one option contains the needle as a substring, it closes the menu (Escape) and throws ArgumentError, because it cannot safely pick which model the user meant.","triggerScenarios":"Calling the kimi model set command with a partial name matching multiple entries, e.g. set='k2' when options include 'K2', 'K2 turbo', 'K2-thinking' (normalized substring matches >1 and no exact match).","commonSituations":"Using abbreviated model names in scripts; Kimi added new variants sharing a prefix; case/spacing differences defeat exact match so several partials remain.","solutions":["Pass the full, exact model name as shown in the dropdown (e.g. 'Kimi K2 Turbo' not 'k2').","List available models first (the list command) and copy an exact option string.","Include distinguishing characters in the query so only one option contains it.","Handle ArgumentError in scripts by prompting for a more specific name."],"exampleFix":"// before\nawait run('kimi', 'model', { set: 'k2' }); // ambiguous\n// after\nawait run('kimi', 'model', { set: 'Kimi K2 Turbo' }); // exact","handlingStrategy":"validation","validationCode":"const available = await run('kimi', 'model'); // list first\nconst exact = available.find(m => m.Model.toLowerCase() === want.toLowerCase());\nif (!exact) throw new Error(`specify one of: ${available.map(m => m.Model).join(', ')}`);","typeGuard":null,"tryCatchPattern":"try {\n  await run('kimi', 'model', { set: want });\n} catch (e) {\n  if (/is ambiguous/.test(e.message)) {\n    const candidates = e.message.split(': ')[1]?.split(', ') ?? [];\n    throw new Error(`Refine the model name; candidates: ${candidates.join(' | ')}`);\n  }\n  throw e;\n}","preventionTips":["Always pass the full model string copied from the model list output.","Avoid ambiguous prefixes in configuration files.","Re-generate model name constants when Kimi adds new variants."],"tags":["argument-validation","ambiguity","kimi"],"backgroundTag":"ambiguous-model-name","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}