{"record":{"id":"79c991373bc7f6e5","repo":"jackwener/OpenCLI","slug":"upwork-sort-must-be-one-of-array-from-sort-value","errorCode":null,"errorMessage":"upwork sort must be one of ${Array.from(SORT_VALUES).join(' / ')}, got \"${value}\"","messagePattern":"upwork sort must be one of (.+?), got \"(.+?)\"","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/upwork/utils.js","lineNumber":99,"sourceCode":"    if (urlMatch) id = urlMatch[0];\n    if (!CIPHERTEXT_PATTERN.test(id)) {\n        throw new ArgumentError(`upwork job id \"${value}\" is not a valid ciphertext (expected ~01… or ~02… followed by digits)`);\n    }\n    return id;\n}\n\nexport function requireFeedTab(value, defaultValue = 'best-matches') {\n    const v = String(value ?? defaultValue).trim().toLowerCase();\n    if (!FEED_TABS[v]) {\n        throw new ArgumentError(`upwork tab must be one of ${Object.keys(FEED_TABS).join(' / ')}, got \"${value}\"`);\n    }\n    return v;\n}\n\nexport function requireSort(value, defaultValue = 'recency') {\n    const v = String(value ?? defaultValue).trim().toLowerCase();\n    if (!SORT_VALUES.has(v)) {\n        throw new ArgumentError(`upwork sort must be one of ${Array.from(SORT_VALUES).join(' / ')}, got \"${value}\"`);\n    }\n    return v;\n}\n\n/**\n * Build the Upwork search URL. Only forwards filters the user actually\n * supplied so the URL stays canonical and round-trippable.\n */\nexport function buildSearchUrl({ query, location, category, sort, page, perPage }) {\n    const params = new URLSearchParams();\n    params.set('q', query);\n    if (location) params.set('location', location);\n    if (category) params.set('category2_uid', category);\n    if (sort && sort !== 'recency') params.set('sort', sort);\n    if (perPage && perPage !== 10) params.set('per_page', String(perPage));\n    if (page && page > 1) params.set('page', String(page));\n    return `${UPWORK_ORIGIN}/nx/search/jobs/?${params.toString()}`;\n}","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/upwork/utils.js#L81-L117","documentation":"requireSort validates the sort option against SORT_VALUES = {recency, relevance, client_total_charge, client_total_reviews}. Any other value throws ArgumentError listing the allowed sort keys.","triggerScenarios":"Calling a search/feed command with --sort set to an unsupported key like 'date', 'rating', or 'client_hire_rate'; typo in one of the snake_case keys (e.g. 'client total charges').","commonSituations":"Using sort names from the Upwork web UI's dropdown which don't match the API keys; assuming camelCase ('clientTotalCharge') works; stale scripts referencing a sort key that was removed or renamed.","solutions":["Use one of: recency, relevance, client_total_charge, client_total_reviews","Omit --sort to use the default 'recency'","Check --help output for the current list of supported sort values"],"exampleFix":"// before\n$ opencli upwork search \"vue\" --sort clientTotalCharge\n// after\n$ opencli upwork search \"vue\" --sort client_total_charge","handlingStrategy":"validation","validationCode":"const SORTS = ['recency','relevance','client_total_charge','client_total_reviews'];\nfunction isValidSort(s) {\n  return SORTS.includes(String(s ?? '').trim().toLowerCase());\n}","typeGuard":"function isSortValue(v) {\n  return ['recency','relevance','client_total_charge','client_total_reviews'].includes(v);\n}","tryCatchPattern":"try {\n  await search({ q, sort });\n} catch (e) {\n  if (e instanceof ArgumentError && /sort must be one of/.test(e.message)) {\n    return search({ q, sort: 'recency' });\n  }\n  throw e;\n}","preventionTips":["Use the snake_case keys exactly as documented","Check --help for the supported sort list per version","Don't assume camelCase or web-UI sort names work"],"tags":["validation","cli","enum","upwork"],"backgroundTag":"invalid-enum-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}