jackwener/OpenCLI · error · CommandExecutionError

Upwork served a Cloudflare challenge page

Error message

Upwork served a Cloudflare challenge page

What it means

If the Vuex-store payload carries a challenge flag, Upwork served a Cloudflare challenge/bot-check page instead of the app. The command throws CommandExecutionError telling the user to clear the challenge manually, since automation cannot (and should not) solve it.

Source

Thrown at clis/upwork/detail.js:86

                const s = window.$nuxt.$store.state.jobDetails;
                return {
                    ready,
                    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 || {};

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open https://www.upwork.com in the connected browser and manually complete the Cloudflare challenge, then retry.
  2. Switch off VPN/proxy or use a residential IP; avoid running from flagged datacenter IPs.
  3. Slow down request frequency / add delays between Upwork commands to avoid tripping rate-based challenges.
  4. Use a regular (non-headless) browser profile with normal history/fingerprint.
  5. If challenges persist, wait and retry later — IP reputation may recover.
Defensive patterns

Strategy: retry

Validate before calling

const res = await fetch('https://www.upwork.com/robots.txt');
if (res.status === 403 || res.status === 503) throw new Error('Cloudflare is challenging this IP; clear it in the browser first');

Try / catch

try {
  const detail = await fetchUpworkJobDetail(id);
} catch (e) {
  if (e instanceof CommandExecutionError && /Cloudflare challenge/.test(e.message)) {
    console.log('Open https://www.upwork.com in the connected browser, solve the challenge, then retry after a delay.');
    await sleep(60_000);
  } else throw e;
}

Prevention

When it happens

Trigger: Fetching Upwork job details when Cloudflare intercepts the request — suspicious IP reputation, datacenter/VPN IP, too-frequent scraping, or first visit from a new browser fingerprint — producing a challenge page whose store payload sets challenge:true.

Common situations: Running the CLI from a VPS/cloud server IP; aggressive repeated scraping triggering rate-based challenges; VPN/proxy usage; browser lacking typical fingerprints (headless); shared IP previously flagged.

Related errors


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