jackwener/OpenCLI · error · CommandExecutionError
Upwork result at rank ${rank} did not include a valid cipher
Error message
Upwork result at rank ${rank} did not include a valid ciphertext id; cannot produce round-trippable detail rows. What it means
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.
Source
Thrown at clis/upwork/utils.js:305
budget: formatBudget(job),
experienceLevel: decodeExperienceLevel(job?.tierText ?? job?.tier),
proposalsTier: decodeProposalsTier(job?.proposalsTier),
skills: formatSkills(job),
clientCountry: country,
clientRating: Number.isFinite(rating) && rating > 0 ? rating : null,
publishedOn: job?.publishedOn || job?.createdOn || '',
url: buildJobUrl(id),
};
}
export function jobsToListRows(jobs, { offset = 0, limit } = {}) {
const rows = [];
const source = limit ? jobs.slice(0, limit) : jobs;
for (const [index, job] of source.entries()) {
const rank = offset + index + 1;
const row = jobToListRow(job, rank);
if (!row) {
throw new CommandExecutionError(`Upwork result at rank ${rank} did not include a valid ciphertext id; cannot produce round-trippable detail rows.`);
}
rows.push(row);
}
return rows;
}
export const LIST_COLUMNS = [
'rank', 'id', 'title', 'type', 'budget',
'experienceLevel', 'proposalsTier', 'skills',
'clientCountry', 'clientRating', 'publishedOn', 'url',
];
export const DETAIL_COLUMNS = [
'id', 'title', 'type', 'budget', 'experienceLevel', 'workload',
'category', 'skills', 'description',
'clientCountry', 'clientSpent', 'clientHires', 'clientRating',
'proposalsCount', 'publishedOn', 'url',
];View on GitHub (pinned to 49907e53dc)
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
Defensive patterns
Strategy: try-catch
Validate before calling
function jobsHaveIds(jobs) {
return jobs.every(j => j && typeof j === 'object' && typeof (j.id ?? j.ciphertext) === 'string' && /^~0[12]/.test(j.id ?? j.ciphertext));
} Type guard
function hasValidCiphertext(job) {
const id = job?.id ?? job?.ciphertext;
return typeof id === 'string' && /^~0[12]\d{15,21}$/.test(id);
} Try / catch
try {
rows = jobsToListRows(jobs, { limit, offset });
} catch (e) {
if (e instanceof CommandExecutionError && /did not include a valid ciphertext/.test(e.message)) {
// drop the bad entry and retry, or warn and continue
return null;
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- xiaohongshu collection API returned a note without stable id
- Booking.com hotel row is missing stable name/url identity
- IMDb redirected to a different title: ${currentId}
- UISDC news page returned an unreadable payload
- UISDC news selector drift: ${reason}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/2d72132d8da8a941.
Report an issue: GitHub.