jackwener/OpenCLI · error · CliError

API_ERROR

API_ERROR

Error message

51job search failed: ${data.message ?? 'unknown'}

What it means

The search command fetches 51job's search JSON API and verifies data.status === '1'/1; any other status throws CliError('API_ERROR') with the API-supplied message (or 'unknown'). This is an upstream API rejection (bad filter codes, throttling, endpoint/format change), thrown before parsing items at clis/51job/search.js:72.

Source

Thrown at clis/51job/search.js:72

        const companySize = resolveCode(kwargs.companySize, COMPANY_SIZE_CODES);
        const sortType = resolveCode(kwargs.sort, SORT_CODES, '0');

        // Establish WAF-clean origin. Reusing the same tab avoids the slider
        // challenge fire every call.
        const currentUrl = await page.evaluate(`(() => window.location.href)()`);
        if (!String(currentUrl).startsWith(WE_ORIGIN)) {
            await navigateTo(page, `${WE_ORIGIN}/pc/search?keyword=${encodeURIComponent(keyword)}&searchType=2`, 2);
        }

        const url = buildSearchUrl({
            keyword, jobArea, salary, workYear, degree,
            companyType, companySize, sortType,
            pageNum, pageSize: Math.min(limit, 50),
        });

        const data = await pageFetchJson(page, url);
        if (data.status !== '1' && data.status !== 1) {
            throw new CliError('API_ERROR', `51job search failed: ${data.message ?? 'unknown'}`);
        }
        const items = data?.resultbody?.job?.items ?? [];
        if (items.length === 0) {
            throw new CliError('NO_DATA', `No jobs matched "${keyword}"`);
        }
        return items.slice(0, limit).map((it, i) => mapJobItem(it, (pageNum - 1) * limit + i + 1));
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the error message — it contains the API's data.message explaining the rejection
  2. Use resolveCode-mapped values: pass filters through the library's accepted code sets (SALARY_CODES, WORKYEAR_CODES, resolveCity) rather than raw strings
  3. Back off and retry with exponential delay if throttled
  4. Update the library if 51job changed the API; verify the status field the check expects

Example fix

// before
await cli.search({ keyword: 'java', salary: '20k-30k', experience: '3 years' });
// after
try {
  return await cli.search({ keyword: 'java', salary: '27', experience: '4' }); // valid codes
} catch (e) {
  if (e.code === 'API_ERROR') { await sleep(5000); return cli.search({ keyword: 'java' }); }
  throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

const KNOWN_CODES = { salary: ['01','02','03',...], experience: ['01','02',...] }; // pass mapped codes only
if (salary && !KNOWN_CODES.salary.includes(salary)) throw new Error('invalid salary code');

Type guard

const isApiOk = (d) => d != null && (d.status === '1' || d.status === 1);

Try / catch

try {
  return await cli.search({ keyword, area, salary, experience });
} catch (e) {
  if (e.code === 'API_ERROR') {
    console.error('51job search API:', e.message);
    await sleep(5000);
    return cli.search({ keyword }); // retry with fewer filters
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the search subcommand when the API rejects the query — invalid salary/experience/workYear or area codes, rate limiting/bot detection, API envelope change, or transient server error.

Common situations: Passing human-readable filter values (e.g. '20k-30k', '3 years') instead of the library's mapped codes; aggressive crawling triggering blocks; 51job changing its API response format so status is no longer '1'; maintenance windows.

Related errors


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