santifer/career-ops · error · Error

nodesk: invalid URL: ${url}

Error message

nodesk: invalid URL: ${url}

What it means

Thrown by nodesk's assertNodeskUrl() when new URL(url) raises an exception — the URL string is syntactically unparseable by the WHATWG URL parser. This is the first gate in a three-stage SSRF guard (valid URL → HTTPS → trusted host) that keeps all fetches pinned to nodesk.co. It fires before any network request is made.

Source

Thrown at providers/nodesk.mjs:20

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

// NoDesk provider - board-wide RSS feed
// (https://nodesk.co/remote-jobs/index.xml). The feed is public, no-auth,
// and XML, so it is parsed in-process with the same tiny tag extractor
// approach as providers/personio.mjs rather than adding an XML dependency.
//
// Wire in via a `job_boards:` entry with `provider: nodesk`.

const FEED_URL = 'https://nodesk.co/remote-jobs/index.xml';
const TRUSTED_HOST = 'nodesk.co';

/** @param {string} url */
function assertNodeskUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`nodesk: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:') throw new Error(`nodesk: URL must use HTTPS: ${url}`);
  if (parsed.hostname !== TRUSTED_HOST) {
    throw new Error(`nodesk: untrusted hostname "${parsed.hostname}" - must be ${TRUSTED_HOST}`);
  }
  return url;
}

// NaN-safe Date.parse - `|| undefined` would also coerce a valid epoch 0.
function toEpochMs(value) {
  if (!value) return undefined;
  const parsed = Date.parse(value);
  return Number.isNaN(parsed) ? undefined : parsed;
}

function fallbackCompany(entry) {
  return typeof entry?.name === 'string' && entry.name.trim() ? entry.name.trim() : 'NoDesk';
}

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Inspect the value being passed to assertNodeskUrl — log or debug the url argument to see the exact malformed string.
  2. If the URL comes from portals.yml, ensure it is a fully-qualified https:// URL (the provider uses the constant FEED_URL by default, so a configured override is the likely culprit).
  3. If the constant FEED_URL itself was edited, restore it to 'https://nodesk.co/remote-jobs/index.xml'.
  4. Add a pre-check in the caller: if (typeof url !== 'string' || !url.startsWith('http')) return null in detect() before reaching fetch().

Example fix

// before
const FEED_URL = 'https://nodesk.co/remote-jobs/index.xml';
// if someone overrides to a bare path:
assertNodeskUrl('nodesk.co/remote-jobs/index.xml'); // throws

// after — ensure a scheme before parsing
function assertNodeskUrl(url) {
  const normalized = typeof url === 'string' && !url.match(/^[a-z]+:///i) ? `https://${url}` : url;
  let parsed;
  try { parsed = new URL(normalized); } catch {
    throw new Error(`nodesk: invalid URL: ${url}`);
  }
  // ... rest of checks
}
Defensive patterns

Strategy: validation

Validate before calling

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

// before calling the provider:
if (!isValidUrlString(entry.api)) {
  console.warn(`nodesk entry ${entry.name} has invalid URL, skipping`);
  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 {
  const jobs = await nodeskProvider.fetch(entry, ctx);
} catch (err) {
  if (String(err.message).startsWith('nodesk: invalid URL')) {
    console.warn(`skipping nodesk entry ${entry.name}: malformed URL`);
    continue;
  }
  throw err;
}

Prevention

When it happens

Trigger: Called with a value that new URL() cannot parse: empty string, undefined coerced to 'undefined', a URL with embedded spaces or control characters, a schemeless bare path like 'remote-jobs/index.xml', or a double-encoded malformed string. In practice this is reached when entry.api or a dynamically built URL is missing/malformed and bypasses an earlier detect() null-return.

Common situations: A portals.yml nodesk entry has careers_url or api set to an empty string, null, or a typo without a scheme (e.g. 'nodesk.co/remote-jobs/index.xml' without https://). Also occurs when a config migration script writes a non-string value that gets stringified to '[object Object]'. Since FEED_URL is a constant, this typically only fires if the constant is changed or assertNodeskUrl is called with a user-supplied URL.

Related errors


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