jackwener/OpenCLI · error · CommandExecutionError

Upwork detail returned an unexpected Browser Bridge payload…

Error message

Upwork detail returned an unexpected Browser Bridge payload shape

What it means

After the login/challenge checks, the command validates that the Browser Bridge payload is a plain object via isPlainObject. If the evaluate returned null, an array, a string, or otherwise non-object data, it throws CommandExecutionError reporting the unexpected payload shape, preventing downstream property access on garbage.

Solutions

  1. Retry with the Upwork job page fully loaded and idle (no pending navigation).
  2. Update the library — mismatched Browser Bridge/injection versions are a frequent cause of shape drift.
  3. Open the job page manually in the connected browser to confirm it renders normally before running the command.
  4. If reproducible on a healthy page, report/inspect: Upwork may have changed its store structure and the extractor needs updating.
  5. Restart the browser bridge connection to rule out stale sessions returning empty results.
Defensive patterns

Strategy: type-guard

Validate before calling

await page.goto(jobUrl, { waitUntil: 'networkidle' });
if (!page.url().includes('upwork.com')) throw new Error('Navigation left Upwork; payload shape cannot be trusted');

Type guard

function isPlainObject(v) {
  if (v === null || typeof v !== 'object') return false;
  const proto = Object.getPrototypeOf(v);
  return proto === Object.prototype || proto === null;
}

Try / catch

try {
  const detail = await fetchUpworkJobDetail(id);
} catch (e) {
  if (e instanceof CommandExecutionError && /unexpected Browser Bridge payload shape/.test(e.message)) {
    await page.waitForNetworkIdle();
    return fetchUpworkJobDetail(id); // retry once after the page settles
  }
  throw e;
}

Prevention

When it happens

Trigger: The in-page extraction IIFE returns something other than a plain object — evaluate cancelled mid-navigation, script exception produced undefined, a Browser Bridge serialization mismatch, or page context returned a primitive.

Common situations: Page navigated/reloaded during evaluate so the result is undefined/null; a version mismatch between CLI extraction script and the injected bridge; Upwork page erroring before the store reader builds its result object; extensions interfering with page scripts.

Related errors


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

Appendix: source

Thrown at clis/upwork/detail.js:89

                    onLogin,
                    challenge,
                    job: s.job ? JSON.parse(JSON.stringify(s.job)) : null,
                    buyer: s.buyer ? JSON.parse(JSON.stringify(s.buyer)) : null,
                };
            })()`));
        }
        catch (e) {
            throw new CommandExecutionError(`Failed to read Upwork job-detail store: ${e?.message ?? e}`, 'The Vuex store 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 (!isPlainObject(payload)) {
            throw new CommandExecutionError('Upwork detail returned an unexpected Browser Bridge payload shape');
        }
        if (!payload?.ready || !payload.job) {
            throw new EmptyResultError('upwork detail', `No Upwork job posting found for id "${id}" (may be closed, expired, or private)`);
        }
        if (!isPlainObject(payload.job)) {
            throw new CommandExecutionError('Upwork job-detail store had an unexpected job shape; expected an object.');
        }

        const job = payload.job;
        const returnedCiphertext = String(job?.ciphertext ?? '').trim();
        if (returnedCiphertext && returnedCiphertext !== id) {
            throw new CommandExecutionError(`Upwork job-detail store returned ciphertext "${returnedCiphertext}" while reading "${id}".`);
        }
        const buyer = payload.buyer || {};
        const stats = buyer?.stats || {};
        const location = buyer?.location || {};
        const category = job?.category?.name || '';
        const skills = formatSkills(job);

View on GitHub (pinned to 49907e53dc)