santifer/career-ops · error · Error
solidjobs: careers_url required
Error message
solidjobs: careers_url required
What it means
The solidjobs provider's fetch() reads the board URL exclusively from entry.careers_url. Unlike providers that can derive an endpoint from a company name, this one has no fallback, so a config entry without careers_url is unusable and the provider fails fast with this message.
Solutions
- Add careers_url: 'https://solid.jobs/public-api/offers/...' to the entry in portals.yml
- Run node doctor.mjs or verify-pipeline.mjs to catch other malformed entries
- Check YAML indentation — careers_url must be a direct child of the entry
Example fix
// before (portals.yml) - name: Example Corp provider: solidjobs # after - name: Example Corp provider: solidjobs careers_url: 'https://solid.jobs/public-api/offers'
Defensive patterns
Strategy: validation
Validate before calling
if (!entry.careers_url || typeof entry.careers_url !== 'string') {
throw new Error(`entry ${entry.name}: solidjobs provider requires careers_url`);
} Type guard
function hasCareersUrl(entry) {
return typeof entry?.careers_url === 'string' && entry.careers_url.length > 0;
} Try / catch
try {
offers = await provider.fetch(entry, ctx);
} catch (e) {
if (/careers_url required/.test(e.message)) {
console.warn(`${entry.name}: add careers_url to portals.yml`);
return [];
}
throw e;
} Prevention
- Require careers_url when adding any solidjobs entry
- Copy full example entries when adding new companies
- Run node doctor.mjs / verify-pipeline.mjs to catch missing fields early
- Watch YAML indentation so nested keys don't silently vanish
When it happens
Trigger: A portals.yml entry claims solidjobs as its provider (or is auto-matched to it) but omits the careers_url field entirely, or sets it to an empty string/null.
Common situations: Adding a new company by copying an existing entry but deleting the URL; a YAML indentation error nesting careers_url under the wrong key so the entry object never sees it; a renamed field after a config schema change.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- arbeitsagentur: entry
- apify: entry missing 'actor' (e.g. misceres/indeed-scraper)
- breezy: cannot derive API URL for
- Cannot read benchmarks at
- config/profile.yml is empty or invalid YAML: fill it in…
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/70097fb5cb190531.
Report an issue: GitHub.
Appendix: source
Thrown at providers/solidjobs.mjs:62
detect(entry) {
const url = entry.careers_url || '';
try {
const parsed = new URL(url);
if (parsed.hostname === 'solid.jobs' && parsed.pathname.startsWith('/public-api/offers/'))
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)