jackwener/OpenCLI · error · CommandExecutionError

Upwork search results did not include any job with a valid c

Error message

Upwork search results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows.

What it means

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.

Source

Thrown at clis/upwork/search.js:111

        if (!payload?.ready) {
            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.');
        }
        if (!isPlainObject(payload)) {
            throw new CommandExecutionError('Upwork search returned an unexpected Browser Bridge payload shape');
        }
        if (!payload.jobsPresent || !Array.isArray(payload.jobs)) {
            throw new CommandExecutionError('Upwork search state had an unexpected jobs shape; expected window.__NUXT__.state.jobsSearch.jobs to be an array.');
        }

        const jobs = payload.jobs;
        if (jobs.length === 0) {
            throw new EmptyResultError('upwork search', `No Upwork jobs matched "${query}"${location ? ` in ${location}` : ''}`);
        }

        const offset = (pageNum - 1) * perPage;
        const rows = jobsToListRows(jobs, { offset, limit: perPage });
        if (rows.length === 0) {
            throw new CommandExecutionError('Upwork search results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows.');
        }
        return rows;
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect a job object from window.__NUXT__.state.jobsSearch.jobs[0] in DevTools to find where the ciphertext id now lives.
  2. Update jobsToListRows' id extraction to the new field name/path.
  3. Retry after a full reload in case a mixed old/new page render produced malformed cards.
  4. If only some jobs lack ids, the error means ALL did - confirm you weren't hitting a test/experimental search variant.

Example fix

// before
const id = job.obscuredId // undefined after schema change -> row skipped
// after
const id = job.obscuredId || job.ciphertext || job.id; // fall back across known id fields before skipping
Defensive patterns

Strategy: type-guard

Validate before calling

// check ids exist before building rows
const allHaveIds = jobs.every(j =>
  j && typeof j === 'object' &&
  typeof (j.obscuredId || j.ciphertext || j.id) === 'string' &&
  (j.obscuredId || j.ciphertext || j.id).length > 0);
if (!allHaveIds) throw new Error('Job payloads lack ciphertext ids; update extraction');

Type guard

function hasCiphertextId(job) {
  const id = job && (job.obscuredId || job.ciphertext || job.id);
  return typeof id === 'string' && id.length > 0;
}

Try / catch

try {
  rows = await cli.search(query);
} catch (e) {
  if (String(e.message).includes('valid ciphertext id')) {
    await dumpFirstJobObject(); // inspect schema, then fix id extraction
    throw e;
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/df4f476893c848f0. Report an issue: GitHub.