santifer/career-ops · error · Error

solidjobs: URL path must start with /public-api/offers/: ${u

Error message

solidjobs: URL path must start with /public-api/offers/: ${url}

What it means

assertUrl's final check requires parsed.pathname to start with '/public-api/offers/'. It fires after parse, https, and host all pass — so the URL is a valid https://solid.jobs URL but points somewhere other than the public offers API (e.g. the marketing landing page, a division listing without the prefix, or the root).

Source

Thrown at providers/solidjobs.mjs:31

 * Validates that the provided URL is a trusted SolidJobs API endpoint.
 * Enforces HTTPS protocol, strict hostname matching, and required path prefix.
 * 
 * @param {string} url - The URL string to validate.
 * @returns {string} The validated URL string.
 * @throws {Error} If the URL is malformed, uses non-HTTPS, has an untrusted host, or wrong path.
 */
function assertUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`solidjobs: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:') throw new Error(`solidjobs: URL must use HTTPS: ${url}`);
  if (!ALLOWED_HOSTS.has(parsed.hostname))
    throw new Error(`solidjobs: untrusted hostname "${parsed.hostname}" — must be solid.jobs`);
  if (!parsed.pathname.startsWith('/public-api/offers/'))
    throw new Error(`solidjobs: URL path must start with /public-api/offers/: ${url}`);
  return url;
}

/** @type {Provider} */
export default {
  id: 'solidjobs',

  /**
   * Attempts to detect if the provider can handle the given entry by checking the careers_url.
   * * @param {{ careers_url?: string, name?: string }} entry - The configuration entry.
   * @returns {{url: string} | null} An object with the matched URL, or null if not matched.
   */
  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 };

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Append the API path and a division: https://solid.jobs/public-api/offers/it
  2. Pick the correct division from the supported list (it, engineering, marketing, sales, hr, logistics, finances, other)
  3. Optionally add ?campaign=<campaign> if the API docs require one

Example fix

# before
careers_url: https://solid.jobs
# after
careers_url: https://solid.jobs/public-api/offers/it
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the API path prefix is present.
function hasOffersPath(v) {
  try { return new URL(v).pathname.startsWith('/public-api/offers/'); } catch { return false; }
}
if (!hasOffersPath(entry.careers_url)) {
  console.warn(`${entry.name}: solidjobs URL must include /public-api/offers/<division>`);
}

Prevention

When it happens

Trigger: entry.careers_url is https://solid.jobs or https://solid.jobs/it rather than https://solid.jobs/public-api/offers/<division>. The API lives under /public-api/offers/ only.

Common situations: The user pasted the browsable careers landing page instead of the API endpoint. Valid divisions are: it, engineering, marketing, sales, hr, logistics, finances, other (per the module header).

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/78c79debaecc8d01. Report an issue: GitHub.