{"record":{"id":"daf1eb598a7e7481","repo":"jackwener/OpenCLI","slug":"upwork-feed-state-had-an-unexpected-jobs-shape-ex","errorCode":null,"errorMessage":"Upwork feed state had an unexpected jobs shape; expected window.__NUXT__.state.${stateKey}.jobs to be an array.","messagePattern":"Upwork feed state had an unexpected jobs shape; expected window\\.__NUXT__\\.state\\.(.+?)\\.jobs to be an array\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/upwork/feed.js","lineNumber":95,"sourceCode":"        }\n        catch (e) {\n            throw new CommandExecutionError(`Failed to read Upwork feed state: ${e?.message ?? e}`, 'The Nuxt state global was not reachable; try again after opening Upwork in the connected browser.');\n        }\n\n        if (payload?.onLogin) {\n            throw new AuthRequiredError('upwork.com', 'Upwork redirected to login. Open https://www.upwork.com in the connected browser and sign in, then retry.');\n        }\n        if (payload?.challenge) {\n            throw new CommandExecutionError('Upwork served a Cloudflare challenge page', 'Open https://www.upwork.com in the connected browser and clear the challenge, then retry.');\n        }\n        if (!payload?.ready) {\n            throw new CommandExecutionError(`Upwork feed state (window.__NUXT__.state.${stateKey}) 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 feed returned an unexpected Browser Bridge payload shape');\n        }\n        if (!payload.jobsPresent || !Array.isArray(payload.jobs)) {\n            throw new CommandExecutionError(`Upwork feed state had an unexpected jobs shape; expected window.__NUXT__.state.${stateKey}.jobs to be an array.`);\n        }\n\n        const jobs = payload.jobs;\n        if (jobs.length === 0) {\n            throw new EmptyResultError(`upwork feed ${tab}`, `Upwork ${tab} feed is empty for the current account`);\n        }\n\n        const rows = jobsToListRows(jobs, { limit });\n        if (rows.length === 0) {\n            throw new CommandExecutionError('Upwork feed 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":77,"sourceCodeEnd":110,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/upwork/feed.js#L77-L110","documentation":"Once ready, the CLI requires window.__NUXT__.state.<feedKey>.jobs to be an actual array (jobsPresent checks the key exists; Array.isArray validates its type). This CommandExecutionError is thrown when the jobs key is missing or is not an array, meaning Upwork's feed state schema no longer matches what the CLI expects — an upstream shape change rather than an empty feed.","triggerScenarios":"Running `upwork feed` when state.feedBestMatch (or feedMostRecent) exists but has no jobs property, or jobs is an object/null/string instead of an array — after an Upwork frontend update that restructures the Nuxt state, or when the state node belongs to a different feed type than requested.","commonSituations":"Upwork A/B tests or redesigns moving jobs into a nested object; requesting a tab whose state key Upwork renamed; a locale/experiment serving a different state shape; stale CLI version against a newer Upwork build.","solutions":["Inspect window.__NUXT__.state.feedBestMatch in DevTools on the open feed page to see the new shape; if jobs moved or was renamed, update the CLI or pin to a compatible Upwork experience.","Retry — A/B bucketing is usually sticky per session, so a fresh profile may get the old shape.","Update @jackwener/opencli to the latest version which may track the new Upwork state schema.","Use the other tab (`upwork feed most-recent`) whose state key may still match, as a workaround."],"exampleFix":"// before\nif (!payload.jobsPresent || !Array.isArray(payload.jobs)) throw new CommandExecutionError(...);\n// after — defensive extraction against a redesigned state\nconst state = payload.state || {};\nconst jobs = Array.isArray(state.jobs) ? state.jobs\n  : Array.isArray(state.feed?.jobs) ? state.feed.jobs\n  : null;\nif (!jobs) throw new CommandExecutionError(`Unexpected jobs shape; got keys: ${Object.keys(state)}`);","handlingStrategy":"validation","validationCode":"// pre-flight: confirm the state key still exists in the live page\nconst hasJobsArray = await bridge.evaluate(\"Array.isArray(window.__NUXT__?.state?.feedBestMatch?.jobs)\");\nif (!hasJobsArray) console.warn('Upwork state schema changed; update the CLI.');","typeGuard":"function hasJobsArray(payload) {\n  return payload != null\n    && Array.isArray(payload.jobs);\n}","tryCatchPattern":"try {\n  const jobs = await upwork.feed();\n} catch (e) {\n  if (/unexpected jobs shape/.test(e.message)) {\n    // upstream schema change: pin an older Upwork experience (fresh profile) or upgrade the CLI\n    return upgradeCliOrInspectState(e);\n  }\n  throw e;\n}","preventionTips":["Update the CLI promptly when Upwork ships redesigns; this error is a drift signal.","Inspect __NUXT__.state in DevTools when it fires to capture the new shape.","Pin/track the opencli package version in CI so behavior is reproducible."],"tags":["upwork","schema-validation-failed","nuxt-state","upstream-schema-change"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}