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
- Inspect window.__NUXT__.state.jobsSearch in the connected browser's DevTools to see the actual shape.
- Retry after a full page load in case hydration was partial.
- If Upwork permanently changed the schema, update the CLI's state extraction to the new key path.
- 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
- After Upwork frontend releases, spot-check window.__NUXT__.state.jobsSearch in DevTools.
- Adapt extraction to fallback key paths when Upwork renames fields.
- Always hit the standard jobs search URL; special page variants use different state shapes.
- Retry once after a full reload in case partial hydration produced a malformed payload.
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
- Barchart greeks returned an unreadable options payload${data
- twitter_likes_protocol_error
- Failed to read Upwork job-detail store: ${e?.message ?? e}
- No Upwork job posting found for id "${id}" (may be closed, e
- Failed to read Upwork search state: ${e?.message ?? e}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/b068a1eedbe9cef6.
Report an issue: GitHub.