{"record":{"id":"cf8b4fa1ba0d2e62","repo":"jackwener/OpenCLI","slug":"weread-official-scope-must-be-one-of-object-ke","errorCode":null,"errorMessage":"weread-official: scope must be one of: ${Object.keys(SEARCH_SCOPES).join(', ')}","messagePattern":"weread-official: scope must be one of: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"warning","filePath":"clis/weread-official/search.js","lineNumber":60,"sourceCode":"    site: 'weread-official',\n    name: 'search',\n    access: 'read',\n    description: 'Search WeRead store via the official agent gateway',\n    domain: 'weread.qq.com',\n    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { name: 'keyword', positional: true, required: true, help: 'Search keyword' },\n        { name: 'scope', default: 'ebook', choices: Object.keys(SEARCH_SCOPES), help: 'Search type (all/ebook/webnovel/audio/author/fulltext/booklist/mp/article)' },\n        { name: 'count', type: 'int', help: 'Page size (gateway default 15 when omitted)' },\n        { name: 'max-idx', type: 'int', default: 0, help: 'Pagination offset, use searchIdx of last item from previous page' },\n    ],\n    columns: ['rank', 'scope', 'bookId', 'title', 'author', 'rating', 'readingCount', 'category', 'searchIdx', 'cover', 'intro', 'link'],\n    func: async (args) => {\n        const keyword = requireText(args.keyword, 'keyword');\n        const scopeKey = String(args.scope ?? 'ebook').trim();\n        if (!Object.prototype.hasOwnProperty.call(SEARCH_SCOPES, scopeKey)) {\n            throw new ArgumentError(\n                `weread-official: scope must be one of: ${Object.keys(SEARCH_SCOPES).join(', ')}`,\n            );\n        }\n        const scope = SEARCH_SCOPES[scopeKey];\n        const params = { keyword, scope, maxIdx: args['max-idx'] ?? 0 };\n        if (args.count !== undefined && args.count !== null && args.count !== '') {\n            params.count = requirePositiveInt(args.count, 'count', { max: 100 });\n        }\n\n        const payload = await callGateway('/store/search', params);\n        const groups = Array.isArray(payload?.results) ? payload.results : [];\n        if (groups.length === 0) {\n            emptyResult('search', `No results for \"${keyword}\" (scope=${scopeKey}).`);\n        }\n\n        const rows = [];\n        let rank = 0;\n        for (const group of groups) {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread-official/search.js#L42-L78","documentation":"Thrown by `weread-official search` when the `scope` argument is not a key of SEARCH_SCOPES (validated with hasOwnProperty after trimming). The scope selects which book catalog the WeRead search API queries (default 'ebook'). Like the type check, this is a case-sensitive enum validation before any request is made.","triggerScenarios":"Passing `--scope` values not in SEARCH_SCOPES: typos ('ebooks'), wrong casing ('Ebook'), or scopes from other tools that aren't supported; passing an empty-but-nonnull value that isn't the default after trim fails to match.","commonSituations":"Copy-pasted flags from documentation of a different weread client; scripts parameterizing scope from config with stale values; users assuming plural or capitalized forms are accepted.","solutions":["Check --help or SEARCH_SCOPES for exact accepted scope keys","Use the default by omitting --scope entirely (defaults to 'ebook')","Match casing exactly — validation is case-sensitive after trim only","Normalize/whitelist scope values in your wrapper before invoking"],"exampleFix":"// before\nawait wereadSearch({ keyword, scope: 'Paper' });\n// after\nconst scopes = ['ebook', 'paper', ...]; // per SEARCH_SCOPES\nconst s = String(scope ?? 'ebook').trim();\nif (!scopes.includes(s)) throw new Error(`scope must be one of: ${scopes.join(', ')}`);\nawait wereadSearch({ keyword, scope: s });","handlingStrategy":"validation","validationCode":"const SCOPES = ['ebook','paper']; // mirror SEARCH_SCOPES\nif (!SCOPES.includes(String(scope ?? 'ebook').trim())) throw new Error(`scope must be one of: ${SCOPES.join(', ')}`);","typeGuard":"function isValidScope(s) { return typeof s === 'string' && Object.keys(SEARCH_SCOPES).includes(s.trim()); }","tryCatchPattern":"try { await wereadSearch(args); }\ncatch (e) { if (e.name === 'ArgumentError' && e.message.includes('scope must be')) { printAllowedScopes(); } else throw e; }","preventionTips":["Omit --scope to use the 'ebook' default","Whitelist scope values in wrapper scripts","Match exact casing from --help"],"tags":["argument-validation","enum","cli"],"backgroundTag":"invalid-enum-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}