santifer/career-ops · error

API error: code=

Error message

API error: code=${json?.code}

What it means

The Feishu Jobs search API wraps every response in an envelope with a code field; code === 0 means success. After each POST to /api/v1/search/job/posts the provider checks json?.code and throws this error when the code is nonzero or missing — covering rate limits, WAF rejections, bad payloads, and malformed responses alike, with only the raw code reported. If jobs were already collected, the error is caught and treated as a soft stop (partial results kept); it propagates only when the very first request of a keyword fails.

Solutions

  1. Read the code value in the message and map it against the tenant's documented API error codes; a 405-like code on jobs.bytedance.com almost always means the User-Agent was changed — keep the provider's MACOS_BROWSER_LIKE_USER_AGENT intact.
  2. Retry later or slow down: reduce max_pages/keywords or honor the built-in inter-page pacing; transient rate-limit codes clear on backoff.
  3. If code=undefined, dump the raw response — you are likely getting an HTML WAF page or empty body instead of the JSON envelope; check network path/proxy and that the URL/origin is correct.
  4. For batch jobs, tolerate partial results: the provider already keeps jobs collected before the failure — structure your caller to accept the partial array instead of treating the throw as total loss.

Example fix

// before: custom fetch call dropping the required UA
json = await ctx.fetchJson(api, { method: 'POST', headers: { 'content-type': 'application/json' }, body });
// after: preserve the macOS Chrome UA and same-origin referer
json = await ctx.fetchJson(api, { method: 'POST', headers: { 'content-type': 'application/json', 'user-agent': MACOS_BROWSER_LIKE_USER_AGENT, 'referer': `${origin}/` }, body });
Defensive patterns

Strategy: retry

Validate before calling

// No pre-call validation can prevent server-side error codes; verify the
// envelope after each response instead:
function assertFeishuEnvelope(json) {
  if (!json || typeof json !== 'object' || json.code !== 0) {
    throw new Error(`feishu: bad envelope code=${json?.code}`);
  }
}

Type guard

const isFeishuSuccess = (json) => !!json && typeof json === 'object' && json.code === 0 && Array.isArray(json?.data?.job_post_list);

Try / catch

try {
  json = await ctx.fetchJson(api, opts);
  if (json?.code !== 0) throw new Error(`API error: code=${json?.code}`);
} catch (err) {
  if (seen.size === 0) throw err; // first page failed — surface it
  console.warn(`Partial results kept: ${seen.size} jobs before failure (${err.message})`);
  return [...seen.values()];
}

Prevention

When it happens

Trigger: The tenant API returns a nonzero code: the UA-sniffing WAF on jobs.bytedance.com rejects a non-macOS-Chrome User-Agent (405), a burst hits rate limiting, the POST body/keyword is rejected, or the endpoint returns HTML/empty JSON so json?.code is undefined (code=undefined).

Common situations: Custom HTTP clients overriding the provider's macOS Chrome UA header (the Windows Chrome UA is verifiably rejected by jobs.bytedance.com); scanning too many keywords/pages without the built-in 300ms inter-page delay; the tenant endpoint changed or is geo-blocked; a proxy returns an error page parsed as JSON without a code field.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/49db24cb5dfa2c5e. Report an issue: GitHub.

Appendix: source

Thrown at providers/feishu-jobs.mjs:174

      for (let page = 1; page <= maxPages; page++) {
        if (firstRequest) firstRequest = false;
        else await sleep(INTER_PAGE_DELAY_MS);
        const offset = (page - 1) * PAGE_SIZE;
        let json;
        try {
          json = /** @type {any} */ (await ctx.fetchJson(api, {
            method: 'POST',
            headers: {
              'content-type': 'application/json',
              'accept': 'application/json',
              'user-agent': MACOS_BROWSER_LIKE_USER_AGENT,
              'referer': `${origin}/`,
            },
            body: JSON.stringify(keyword ? { limit: PAGE_SIZE, offset, keyword } : { limit: PAGE_SIZE, offset }),
            redirect: 'error',
          }));
          if (json?.code !== 0) {
            throw new Error(`API error: code=${json?.code}`);
          }
        } catch (err) {
          if (seen.size === 0) throw err;
          console.error(`  ⚠ feishu-jobs: keyword "${keyword}" page ${page} failed (${err.message}) — keeping the ${seen.size} jobs collected so far`);
          return [...seen.values()];
        }
        const companyName = entry.name || origin;
        const sourcePage = Array.isArray(json?.data?.job_post_list) ? json.data.job_post_list : [];
        const { jobs, total } = parseFeishuJobsResponse(json, companyName, origin);
        if (sourcePage.length === 0) break;

        for (const job of jobs) {
          if (!seen.has(job.url)) seen.set(job.url, job);
        }

        const covered = Math.min(offset + PAGE_SIZE, total);
        if (covered >= total) break;
        if (page === maxPages && probeMaxPages > entryMaxPages) {

View on GitHub (pinned to aac998c7ed)