{"record":{"id":"ee3a230dba4ece90","repo":"jackwener/OpenCLI","slug":"upwork-search-state-window-nuxt-state-jobssea","errorCode":null,"errorMessage":"Upwork search state (window.__NUXT__.state.jobsSearch) was not present within 15s","messagePattern":"Upwork search state \\(window\\.__NUXT__\\.state\\.jobsSearch\\) was not present within 15s","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/upwork/search.js","lineNumber":94,"sourceCode":"                    jobsPresent: !!(state && Object.prototype.hasOwnProperty.call(state, 'jobs')),\n                    jobs: state ? state.jobs : undefined,\n                    paging: state && state.paging ? state.paging : null,\n                    status: state ? state.status : null,\n                };\n            })()`));\n        }\n        catch (e) {\n            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.');\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 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.');\n        }\n        if (!isPlainObject(payload)) {\n            throw new CommandExecutionError('Upwork search returned an unexpected Browser Bridge payload shape');\n        }\n        if (!payload.jobsPresent || !Array.isArray(payload.jobs)) {\n            throw new CommandExecutionError('Upwork search state had an unexpected jobs shape; expected window.__NUXT__.state.jobsSearch.jobs to be an array.');\n        }\n\n        const jobs = payload.jobs;\n        if (jobs.length === 0) {\n            throw new EmptyResultError('upwork search', `No Upwork jobs matched \"${query}\"${location ? ` in ${location}` : ''}`);\n        }\n\n        const offset = (pageNum - 1) * perPage;\n        const rows = jobsToListRows(jobs, { offset, limit: perPage });\n        if (rows.length === 0) {\n            throw new CommandExecutionError('Upwork search results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows.');\n        }","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/upwork/search.js#L76-L112","documentation":"A CommandExecutionError thrown when the Browser Bridge poll completes but window.__NUXT__.state.jobsSearch never appeared within the 15s timeout (payload.ready false). The page loaded enough to not redirect or challenge, but the SSR/hydration state for the jobs search never materialized. The library gives up after 15 seconds rather than hanging.","triggerScenarios":"Running upwork search against a page that hydrates slowly, a page where Upwork changed the __NUXT__ state shape so jobsSearch no longer exists, or a search URL that renders no jobsSearch state (e.g. not actually on a search results page).","commonSituations":"Slow network or heavy page causing hydration past 15s; an Upwork frontend update renaming/moving jobsSearch in the Nuxt payload; landing on a login-adjacent or error page that still isn't a challenge; CPU-starved browser taking long to hydrate.","solutions":["Retry the command once the page has fully finished loading in the connected browser.","Verify the connected tab is actually on an Upwork jobs search results page.","If it reproduces on a fully loaded page, inspect window.__NUXT__.state in DevTools to see if the jobsSearch key moved - update the CLI's expectations if Upwork changed shape.","Improve network/CPU conditions (close heavy tabs, faster connection) and increase patience before retrying."],"exampleFix":"// before\nawait cli.search('vue developer') // throws: state not present within 15s\n// after\nawait page.waitForFunction(() => window.__NUXT__?.state?.jobsSearch, { timeout: 30000 }); // or retry the command after full load\nawait cli.search('vue developer')","handlingStrategy":"retry","validationCode":"// wait for hydration before invoking, or confirm the page is a search results page\nawait page.waitForFunction(() => !!(window.__NUXT__ && window.__NUXT__.state), { timeout: 30000 });\nif (!location.pathname.includes('/jobs')) throw new Error('Navigate to an Upwork jobs search page first');","typeGuard":"function isNuxtStateReady(win) {\n  return !!(win && win.__NUXT__ && win.__NUXT__.state &&\n    typeof win.__NUXT__.state === 'object');\n}","tryCatchPattern":"try {\n  rows = await cli.search(query);\n} catch (e) {\n  if (String(e.message).includes('was not present within 15s')) {\n    await waitForHydration(60000); // give the page more time\n    rows = await cli.search(query); // retry once\n  } else throw e;\n}","preventionTips":["Run searches against a fully loaded page on a reliable connection.","Confirm the tab is on a real jobs search results URL, not an intermediate page.","Inspect window.__NUXT__.state after Upwork deploys; key renames break readiness checks.","Close CPU-heavy tabs so hydration completes within the timeout."],"tags":["timeout","hydration","upwork","scraping"],"backgroundTag":"ssr-state-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}