santifer/career-ops · error · Error

nofluffjobs: untrusted hostname

Error message

nofluffjobs: untrusted hostname "${parsed.hostname}" — must be nofluffjobs.com

What it means

assertNoFluffUrl restricts hostnames to the ALLOWED_HOSTS set (nofluffjobs.com and its official API hosts). This error is thrown when parsed.hostname is not in that set, blocking third-party, look-alike, or typo'd domains from being queried through this provider.

Solutions

  1. Use a hostname that is in ALLOWED_HOSTS (check the constant at the top of providers/nofluffjobs.mjs for the exact list)
  2. If a regional variant is legitimately needed, add it to ALLOWED_HOSTS deliberately and review the change
  3. Move entries for other companies/providers to their correct provider config
  4. Fix typos and stray subdomains in the configured URL

Example fix

// before
assertNoFluffUrl('https://jobs.example.com/api');
// after
assertNoFluffUrl('https://nofluffjobs.com/api/search/posting');
Defensive patterns

Strategy: validation

Validate before calling

const parsed = new URL(url);
if (!ALLOWED_HOSTS.has(parsed.hostname)) throw new Error(`host not allowed: ${parsed.hostname}`);

Type guard

function isAllowedHost(u, allowed) {
  try { return allowed.has(new URL(u).hostname); } catch { return false; }
}

Try / catch

try {
  await nofluffjobs.fetch(entry, ctx);
} catch (e) {
  if (String(e.message).startsWith('nofluffjobs: untrusted hostname')) {
    log.warn(`Entry for ${entry.company} is not a nofluffjobs.com URL; reassign to correct provider`);
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: A careers_url/api entry whose hostname is not in ALLOWED_HOSTS — e.g. 'www.nofluffjobs.de' if only the .com host is allowed, a country subdomain, a staging host, or an entirely different company's board.

Common situations: Copy-paste from another provider's config; regional TLD variants (nofluffjobs.pl/hu/de) not in the allowlist; host renamed after a vendor change; typo like 'nofluffjobs.com.evil.io'.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at providers/nofluffjobs.mjs:23

// 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;
  }
}

function normalizeLocation(posting) {
  const parts = [];
  if (posting?.fullyRemote || posting?.location?.fullyRemote) parts.push('Remote');
  if (Array.isArray(posting?.location?.places)) {

View on GitHub (pinned to aac998c7ed)