{"record":{"id":"2d72132d8da8a941","repo":"jackwener/OpenCLI","slug":"upwork-result-at-rank-rank-did-not-include-a-va","errorCode":null,"errorMessage":"Upwork result at rank ${rank} did not include a valid ciphertext id; cannot produce round-trippable detail rows.","messagePattern":"Upwork result at rank (.+?) did not include a valid ciphertext id; cannot produce round-trippable detail rows\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/upwork/utils.js","lineNumber":305,"sourceCode":"        budget: formatBudget(job),\n        experienceLevel: decodeExperienceLevel(job?.tierText ?? job?.tier),\n        proposalsTier: decodeProposalsTier(job?.proposalsTier),\n        skills: formatSkills(job),\n        clientCountry: country,\n        clientRating: Number.isFinite(rating) && rating > 0 ? rating : null,\n        publishedOn: job?.publishedOn || job?.createdOn || '',\n        url: buildJobUrl(id),\n    };\n}\n\nexport function jobsToListRows(jobs, { offset = 0, limit } = {}) {\n    const rows = [];\n    const source = limit ? jobs.slice(0, limit) : jobs;\n    for (const [index, job] of source.entries()) {\n        const rank = offset + index + 1;\n        const row = jobToListRow(job, rank);\n        if (!row) {\n            throw new CommandExecutionError(`Upwork result at rank ${rank} did not include a valid ciphertext id; cannot produce round-trippable detail rows.`);\n        }\n        rows.push(row);\n    }\n    return rows;\n}\n\nexport const LIST_COLUMNS = [\n    'rank', 'id', 'title', 'type', 'budget',\n    'experienceLevel', 'proposalsTier', 'skills',\n    'clientCountry', 'clientRating', 'publishedOn', 'url',\n];\n\nexport const DETAIL_COLUMNS = [\n    'id', 'title', 'type', 'budget', 'experienceLevel', 'workload',\n    'category', 'skills', 'description',\n    'clientCountry', 'clientSpent', 'clientHires', 'clientRating',\n    'proposalsCount', 'publishedOn', 'url',\n];","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/upwork/utils.js#L287-L323","documentation":"jobsToListRows maps raw Upwork job results to round-trippable list rows and throws CommandExecutionError when a job at a given rank yields no row via jobToListRow — meaning the payload had no valid ciphertext id, so the detail command could never re-open that job. This fails fast rather than emitting rows that break the id round-trip contract.","triggerScenarios":"Upwork's __NUXT__ payload contains a job entry missing or malformed ciphertext id (e.g. promoted/ad cards, removed jobs still in the payload, partially rendered SSR state); scraping/structural changes to the payload shape.","commonSituations":"Upwork A/B tests or layout changes injecting non-job cards into results; jobs unpublished between listing and rendering; Cloudflare-challenge pages or logged-out sessions yielding degraded payloads; ad slots appearing in 'most-recent' feed.","solutions":["Re-run the command — transient ad/removed entries often disappear","Verify you are logged in (session cookie valid); anonymous payloads are degraded","Narrow filters or reduce the limit to skip the problematic rank","Upgrade the adapter if Upwork changed the payload shape","Report the rank/URL so jobToListRow can be taught to skip such entries"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"function jobsHaveIds(jobs) {\n  return jobs.every(j => j && typeof j === 'object' && typeof (j.id ?? j.ciphertext) === 'string' && /^~0[12]/.test(j.id ?? j.ciphertext));\n}","typeGuard":"function hasValidCiphertext(job) {\n  const id = job?.id ?? job?.ciphertext;\n  return typeof id === 'string' && /^~0[12]\\d{15,21}$/.test(id);\n}","tryCatchPattern":"try {\n  rows = jobsToListRows(jobs, { limit, offset });\n} catch (e) {\n  if (e instanceof CommandExecutionError && /did not include a valid ciphertext/.test(e.message)) {\n    // drop the bad entry and retry, or warn and continue\n    return null;\n  }\n  throw e;\n}","preventionTips":["Ensure a logged-in session so payloads render fully","Re-run on transient failures (ads/removed jobs)","Keep the adapter updated for Upwork payload changes","Skip ad/promoted entries before mapping rows"],"tags":["upwork","scraping","data-integrity","command-execution-error"],"backgroundTag":"scraper-payload-shape-change","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}