{"record":{"id":"df4f476893c848f0","repo":"jackwener/OpenCLI","slug":"upwork-search-results-did-not-include-any-job-with","errorCode":null,"errorMessage":"Upwork search results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows.","messagePattern":"Upwork search results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/upwork/search.js","lineNumber":111,"sourceCode":"        if (!payload?.ready) {\n            throw new CommandExecutionError('Upwork search state (window.__NUXT__.state.jobsSearch) was not present within 15s', 'The page may not have finished hydrating, or the SSR state shape may have changed.');\n        }\n        if (!isPlainObject(payload)) {\n            throw new CommandExecutionError('Upwork search returned an unexpected Browser Bridge payload shape');\n        }\n        if (!payload.jobsPresent || !Array.isArray(payload.jobs)) {\n            throw new CommandExecutionError('Upwork search state had an unexpected jobs shape; expected window.__NUXT__.state.jobsSearch.jobs to be an array.');\n        }\n\n        const jobs = payload.jobs;\n        if (jobs.length === 0) {\n            throw new EmptyResultError('upwork search', `No Upwork jobs matched \"${query}\"${location ? ` in ${location}` : ''}`);\n        }\n\n        const offset = (pageNum - 1) * perPage;\n        const rows = jobsToListRows(jobs, { offset, limit: perPage });\n        if (rows.length === 0) {\n            throw new CommandExecutionError('Upwork search results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows.');\n        }\n        return rows;\n    },\n});\n","sourceCodeStart":93,"sourceCodeEnd":116,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/upwork/search.js#L93-L116","documentation":"A CommandExecutionError thrown when the search returned jobs but jobsToListRows produced zero rows because none of the job entries carried a valid ciphertext id. The CLI requires round-trippable ids so each list row can be opened with the detail command, and it refuses to emit rows that cannot be resolved later.","triggerScenarios":"Running upwork search when every job object in the payload lacks the expected ciphertext id field - typically because Upwork changed the job object schema or the id extraction regex/field name no longer matches.","commonSituations":"An Upwork API/SSR change altering how job identifiers (e.g. ~01abc... ciphertexts) are embedded in the state; promoted/featured job cards using a different object shape; parsing jobsToListRows failing on a new card layout.","solutions":["Inspect a job object from window.__NUXT__.state.jobsSearch.jobs[0] in DevTools to find where the ciphertext id now lives.","Update jobsToListRows' id extraction to the new field name/path.","Retry after a full reload in case a mixed old/new page render produced malformed cards.","If only some jobs lack ids, the error means ALL did - confirm you weren't hitting a test/experimental search variant."],"exampleFix":"// before\nconst id = job.obscuredId // undefined after schema change -> row skipped\n// after\nconst id = job.obscuredId || job.ciphertext || job.id; // fall back across known id fields before skipping","handlingStrategy":"type-guard","validationCode":"// check ids exist before building rows\nconst allHaveIds = jobs.every(j =>\n  j && typeof j === 'object' &&\n  typeof (j.obscuredId || j.ciphertext || j.id) === 'string' &&\n  (j.obscuredId || j.ciphertext || j.id).length > 0);\nif (!allHaveIds) throw new Error('Job payloads lack ciphertext ids; update extraction');","typeGuard":"function hasCiphertextId(job) {\n  const id = job && (job.obscuredId || job.ciphertext || job.id);\n  return typeof id === 'string' && id.length > 0;\n}","tryCatchPattern":"try {\n  rows = await cli.search(query);\n} catch (e) {\n  if (String(e.message).includes('valid ciphertext id')) {\n    await dumpFirstJobObject(); // inspect schema, then fix id extraction\n    throw e;\n  } else throw e;\n}","preventionTips":["Pin-test one known job object's shape after Upwork deploys.","Support multiple known id field names (obscuredId, ciphertext, id) in extraction.","Never emit rows without ids - keep the round-trip guarantee intact.","Check DevTools for promoted/featured card variants that carry ids differently."],"tags":["schema-change","upwork","id-extraction","round-trip"],"backgroundTag":"unexpected-payload-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}