santifer/career-ops · error · Error

solidjobs: unexpected API response — expected

Error message

solidjobs: unexpected API response — expected { jobs: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]

What it means

After fetching, the provider asserts the response is a JSON object with a jobs array (expected shape { jobs: [...] }). If the JSON is null, not an object, or lacks an array-valued jobs key, it throws, embedding the actual top-level keys (or 'null') to aid debugging. This catches API drift, HTML error pages parsed as junk, and proxy/auth walls.

Solutions

  1. Log the printed key list to see what the endpoint actually returned and adapt
  2. Confirm the careers_url points at the offers API (not another solidjobs endpoint)
  3. Check whether solid.jobs changed its public API contract and update the provider's parsing
  4. Retry later if it is a transient rate-limit/WAF response

Example fix

// endpoint drifted
// before: { jobs: [...] }
// after: { data: [...] } — update provider to read json.data || json.jobs
const jobs = json.jobs ?? json.data;
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

function hasExpectedShape(json) {
  return !!json && typeof json === 'object' && Array.isArray(json.jobs);
}

Try / catch

try {
  offers = await provider.fetch(entry, ctx);
} catch (e) {
  if (e.message.includes('unexpected API response')) {
    console.error(`${entry.name}: solidjobs API shape changed or was blocked — inspect keys in message`);
    return [];
  }
  throw e;
}

Prevention

When it happens

Trigger: solid.jobs changes its API contract; a Cloudflare/auth wall returns JSON like { error: 'forbidden' }; fetchJson returns null on an empty body; an HTML error page fails JSON parsing upstream and json arrives null.

Common situations: Endpoint works but a newer API version returns { data: [...] } or { offers: [...] }; rate limiting returning a JSON error object; misconfigured careers_url hitting a different endpoint that returns valid JSON of the wrong shape.

Related errors


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

Appendix: source

Thrown at providers/solidjobs.mjs:67

        return { url };
    } catch {}
    return null;
  },
  
  /**
   * Fetches and normalizes job offers from the SolidJobs public API.
   * * @param {{ careers_url?: string, name: string }} entry - The configuration entry being processed.
   * @param {{ fetchJson: (url: string, opts?: { redirect?: 'error'|'follow'|'manual' }) => Promise<any> }} ctx - HTTP context.
   * @returns {Promise<Array<{title: string, url: string, company: string, location: string}>>} Array of parsed job offers.
   */
  async fetch(entry, ctx) {
    const url = entry.careers_url;
    if (!url) throw new Error('solidjobs: careers_url required');
    assertUrl(url);
    // redirect:'error' prevents SSRF via server-side redirects
    const json = await ctx.fetchJson(url, { redirect: 'error' });
    if (!json || !Array.isArray(json.jobs)) {
      throw new Error(`solidjobs: unexpected API response — expected { jobs: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`);
    }

    /** @type {Array<{ title?: string, url?: string, company?: string, locations?: string | string[] }>} */
    const jobs = json.jobs;

    return jobs
      .filter(j => j && typeof j === 'object' && typeof j.url === 'string' && j.url.trim() !== '')
      .map(j => ({
        title: j.title || '',
        url: /** @type {string} */ (j.url || '').trim(),
        company: j.company || entry.name,
        location: Array.isArray(j.locations) ? j.locations.join(', ') : (typeof j.locations === 'string' ? j.locations : ''),
      }));
  },
};

View on GitHub (pinned to aac998c7ed)