{"record":{"id":"6e3b26499cc37902","repo":"jackwener/OpenCLI","slug":"crates-search","errorCode":null,"errorMessage":"crates search","messagePattern":"crates search","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/crates/search.js","lineNumber":30,"sourceCode":"    name: 'search',\n    access: 'read',\n    description: 'Search the public crates.io registry by keyword',\n    domain: 'crates.io',\n    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { name: 'query', positional: true, required: true, help: 'Search keyword (e.g. \"serde\", \"async runtime\")' },\n        { name: 'limit', type: 'int', default: 20, help: 'Max results (1-100)' },\n    ],\n    columns: ['rank', 'name', 'latestVersion', 'description', 'downloads', 'recentDownloads', 'repository', 'updated', 'url'],\n    func: async (args) => {\n        const query = requireString(args.query, 'query');\n        const limit = requireBoundedInt(args.limit, 20, 100);\n        const url = `${CRATES_BASE}/api/v1/crates?q=${encodeURIComponent(query)}&per_page=${limit}`;\n        const body = await cratesFetch(url, 'crates search');\n        const list = Array.isArray(body?.crates) ? body.crates : [];\n        if (!list.length) {\n            throw new EmptyResultError('crates search', `No crates.io results matched \"${query}\".`);\n        }\n        return list.slice(0, limit).map((c, i) => ({\n            rank: i + 1,\n            name: String(c.name ?? c.id ?? ''),\n            latestVersion: String(c.newest_version ?? c.max_stable_version ?? c.max_version ?? ''),\n            description: String(c.description ?? '').trim(),\n            downloads: c.downloads != null ? Number(c.downloads) : null,\n            recentDownloads: c.recent_downloads != null ? Number(c.recent_downloads) : null,\n            repository: String(c.repository ?? c.homepage ?? ''),\n            updated: String(c.updated_at ?? '').slice(0, 10),\n            url: c.name ? `https://crates.io/crates/${c.name}` : '',\n        }));\n    },\n});\n","sourceCodeStart":12,"sourceCodeEnd":45,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/crates/search.js#L12-L45","documentation":"The 'crates search' EmptyResultError is thrown when the crates.io search API returns an empty crates list for the given query. The adapter treats 'zero matches' as a distinct typed result so callers can render a friendly 'no results' message instead of an empty array or a generic error.","triggerScenarios":"Calling `crates search <query>` with a term that matches no crates on crates.io, an overly specific query, or a misspelled keyword; requireString/query validation passed but body.crates is an empty array.","commonSituations":"Searching for an internal or unpublished crate name, using keywords in a language crates.io does not index, pasting a full crate URL as the query, or searching a very new crate before indexing.","solutions":["Broaden the query (shorter, more generic keywords).","Check spelling; run the same query on crates.io search in a browser to confirm zero matches.","If you know the exact name, use `crates crate <name>` directly instead of search.","Catch EmptyResultError and display 'no crates matched' rather than treating it as a failure."],"exampleFix":"// before\nconst res = await cli.crates.search({ query: 'my-internal-only-crate' });\n// after\nconst res = await cli.crates.search({ query: 'serde' }); // or handle empty:\ntry { ... } catch (e) { if (e instanceof EmptyResultError) show('no matches'); }","handlingStrategy":"fallback","validationCode":"const q = (query ?? '').trim();\nif (!q) throw new Error('query required');\n// optionally pre-check breadth: very long or highly specific queries often return 0 hits","typeGuard":"function isUsableQuery(v) {\n  return typeof v === 'string' && v.trim().length > 0 && v.trim().length <= 256;\n}","tryCatchPattern":"try {\n  const res = await cli.crates.search({ query });\n  return res;\n} catch (e) {\n  if (e instanceof EmptyResultError) {\n    console.warn(`No crates matched \"${query}\"; broadening query...`);\n    return cli.crates.search({ query: query.split(/\\s+/)[0] }); // fall back to first keyword\n  }\n  throw e;\n}","preventionTips":["Start with short, generic keywords; narrow down in a second query.","Confirm zero-match behavior by running the query on crates.io first.","Handle EmptyResultError explicitly in user-facing tools.","Do not paste URLs or descriptions as search queries."],"tags":["empty-result","crates-io","search"],"backgroundTag":"empty-result-set","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}