santifer/career-ops · error · Error

nofluffjobs: invalid URL: ${url}

Error message

nofluffjobs: invalid URL: ${url}

What it means

Thrown by nofluffjobs' assertNoFluffUrl() when new URL(url) throws — the URL is syntactically invalid. This is the first gate of a three-stage SSRF guard (valid URL → HTTPS → trusted host) that constrains all NoFluffJobs API calls to nofluffjobs.com. The guard is invoked from both detectUrl() and fetch().

Source

Thrown at providers/nofluffjobs.mjs:19

// @ts-check
/** @typedef {import('./_types.js').Provider} Provider */

// NoFluffJobs provider — hits the public search posting API.
// It intentionally returns only the core scanner job fields; richer skill and
// salary metadata can be added later if the provider contract is expanded.

const ALLOWED_HOSTS = new Set(['nofluffjobs.com']);
const API_URL = 'https://nofluffjobs.com/api/search/posting';
const JOB_BASE = 'https://nofluffjobs.com/pl/job/';
const PAGE_SIZE = 20;
const MAX_PAGES = 5;

function assertNoFluffUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`nofluffjobs: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:') throw new Error(`nofluffjobs: URL must use HTTPS: ${url}`);
  if (!ALLOWED_HOSTS.has(parsed.hostname)) {
    throw new Error(`nofluffjobs: untrusted hostname "${parsed.hostname}" — must be nofluffjobs.com`);
  }
  return parsed;
}

function detectUrl(entry) {
  const url = entry.api || entry.careers_url || '';
  if (typeof url !== 'string' || !url.trim()) return null;
  try {
    return { url: assertNoFluffUrl(url).href };
  } catch {
    return null;
  }
}

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Log the url value passed to assertNoFluffUrl to identify the malformed string.
  2. Ensure the portals.yml entry for nofluffjobs has api or careers_url as a fully-qualified https://nofluffjobs.com/... URL.
  3. If no api/careers_url is needed (the provider uses API_URL constant internally), remove the misconfigured field so detectUrl() returns the default.

Example fix

// before
// portals.yml has a typo or missing scheme:
job_boards:
  nofluff:
    provider: nofluffjobs
    api: 'nofluffjobs.com/api/search/posting'  // missing https://

// after
job_boards:
  nofluff:
    provider: nofluffjobs
    api: 'https://nofluffjobs.com/api/search/posting'
Defensive patterns

Strategy: validation

Validate before calling

/** Validate a URL string is parseable before passing to assertNoFluffUrl. */
function isValidUrlString(url) {
  return typeof url === 'string'
    && url.length > 0
    && /^https?:\/\/.+/i.test(url)
    && (() => { try { new URL(url); return true; } catch { return false; } })();
}

if (!isValidUrlString(entry.api) && !isValidUrlString(entry.careers_url)) {
  console.warn(`nofluffjobs entry ${entry.name} has no valid URL`);
  continue;
}

Type guard

/** @param {unknown} url @returns {url is string} */
function isParseableUrl(url) {
  if (typeof url !== 'string' || !url) return false;
  try { new URL(url); return true; } catch { return false; }
}

Try / catch

try {
  await nofluffProvider.fetch(entry, ctx);
} catch (err) {
  if (String(err.message).startsWith('nofluffjobs: invalid URL')) {
    console.warn(`skipping nofluffjobs entry ${entry.name}: malformed URL`);
    continue;
  }
  throw err;
}

Prevention

When it happens

Trigger: Called with an unparseable URL string: empty string, undefined, a value with spaces/control characters, or a schemeless path like 'nofluffjobs.com/api/search/posting'. In detectUrl() this is caught and returns null, but direct calls to assertNoFluffUrl() (e.g. from fetch() or tests) surface the throw.

Common situations: A portals.yml entry has api or careers_url left empty, set to null, or containing a typo. A config migration or YAML parsing edge case produces a non-string value. A developer passes a relative path expecting it to be resolved against a base URL (this provider does no base resolution).

Related errors


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