jackwener/OpenCLI · error · CommandExecutionError

Upwork search state had an unexpected jobs shape; expected w

Error message

Upwork search state had an unexpected jobs shape; expected window.__NUXT__.state.jobsSearch.jobs to be an array.

What it means

A CommandExecutionError thrown when payload.jobsPresent is falsy or payload.jobs is not an array - i.e. the Nuxt state exists but its jobsSearch.jobs field is missing or of the wrong type. The library throws instead of returning garbage rows because every result row must carry a round-trippable job id.

Source

Thrown at clis/upwork/search.js:100

        }
        catch (e) {
            throw new CommandExecutionError(`Failed to read Upwork search state: ${e?.message ?? e}`, 'The Nuxt state global was not reachable; try again after opening Upwork in the connected browser.');
        }

        if (payload?.onLogin) {
            throw new AuthRequiredError('upwork.com', 'Upwork redirected to login. Open https://www.upwork.com in the connected browser and sign in, then retry.');
        }
        if (payload?.challenge) {
            throw new CommandExecutionError('Upwork served a Cloudflare challenge page', 'Open https://www.upwork.com in the connected browser and clear the challenge, then retry.');
        }
        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 window.__NUXT__.state.jobsSearch in the connected browser's DevTools to see the actual shape.
  2. Retry after a full page load in case hydration was partial.
  3. If Upwork permanently changed the schema, update the CLI's state extraction to the new key path.
  4. Ensure you are hitting the standard Upwork jobs search URL rather than a special page variant.

Example fix

// before
const jobs = state.jobsSearch.jobs // undefined after Upwork renamed the field
// after
const jobs = state.jobsSearch.jobs || state.jobsSearch.searchResults?.jobs; // adapt to new schema, and throw the explicit shape error if still absent
Defensive patterns

Strategy: type-guard

Validate before calling

// verify the state shape before trusting results
const shapeOk = await bridge.eval(() => {
  const js = window.__NUXT__ && window.__NUXT__.state && window.__NUXT__.state.jobsSearch;
  return !!js && Array.isArray(js.jobs);
});
if (!shapeOk) throw new Error('Upwork state shape changed; update extraction');

Type guard

function hasJobsArray(state) {
  const js = state && state.jobsSearch;
  return !!js && Array.isArray(js.jobs);
}

Try / catch

try {
  rows = await cli.search(query);
} catch (e) {
  if (String(e.message).includes('unexpected jobs shape')) {
    await dumpNuxtStateForInspection(); // capture __NUXT__.state for schema debugging
    throw e; // requires a code fix once Upwork's schema is known
  } else throw e;
}

Prevention

When it happens

Trigger: Running upwork search when Upwork's SSR state shape changed (jobsSearch exists but jobs renamed/removed), or the page reached a state where jobsSearch is present but holds no jobs array (e.g. an error variant of the search page).

Common situations: An Upwork frontend rollout altering the __NUXT__.state.jobsSearch schema; querying a search URL variant that produces a different state shape; a partially hydrated page where jobsSearch exists but jobs is undefined.

Related errors


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