santifer/career-ops · error · Error

remotli: invalid URL

Error message

remotli: invalid URL: ${url}

What it means

assertRemotliUrl() guards the Remotli API URL before any server-side fetch: the URL must parse with the URL constructor, else this error is thrown (followed by HTTPS and remotli.ch-only host checks). It is part of the provider's SSRF defence.

Solutions

  1. Prefix the value with https:// if only a hostname was provided
  2. Trim whitespace/quotes and correct typos in careers_url
  3. Validate locally with new URL(raw) before committing the config

Example fix

// before
careers_url: remotli.ch/api
// after
careers_url: https://remotli.ch/api
Defensive patterns

Strategy: validation

Validate before calling

function isRemotliUrl(s) {
  try { const u = new URL(s); return u.protocol === 'https:' && u.hostname === 'remotli.ch'; } catch { return false; }
}
if (!isRemotliUrl(entry.careers_url)) throw new Error(`careers_url must be https://remotli.ch/... for ${entry.name}`);

Type guard

function isValidHttpsUrl(v) {
  try { return new URL(String(v)).protocol === 'https:'; } catch { return false; }
}

Try / catch

try {
  assertRemotliUrl(entry.careers_url);
} catch (err) {
  if (err.message.startsWith('remotli: invalid URL')) {
    console.error(`Unparseable careers_url "${entry.careers_url}" — add https:// scheme and trim whitespace`);
  }
  throw err;
}

Prevention

When it happens

Trigger: assertRemotliUrl() (via fetch or detect) receives an unparseable URL string — missing scheme, empty string, illegal characters, whitespace, or a placeholder value.

Common situations: A portals.yml entry stores the bare host (remotli.ch) without https://; copy-paste artifacts (spaces, quotes); an empty or template-placeholder careers_url field.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at providers/remotli.mjs:239

  const description = htmlToText(job.description);
  if (description) out.description = description;

  const postedAt = toEpochMs(job.publishedAt || job.createdAt);
  if (postedAt !== undefined) out.postedAt = postedAt;

  const salary = resolveSalary(job);
  if (salary) out.salary = salary;

  return out;
}

/** Guard the API URL: HTTPS + remotli.ch only. */
function assertRemotliUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`remotli: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:') throw new Error(`remotli: URL must use HTTPS: ${url}`);
  if (!HOST_RE.test(parsed.hostname))
    throw new Error(`remotli: untrusted hostname "${parsed.hostname}" — must be remotli.ch`);
  return url;
}

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

  detect(entry) {
    const raw = typeof entry.careers_url === 'string' ? entry.careers_url : '';
    if (!raw) return null;
    let parsed;
    try {
      parsed = new URL(raw);
    } catch {

View on GitHub (pinned to aac998c7ed)