santifer/career-ops · error · Error
teamtailor: invalid URL: ${url}
Error message
teamtailor: invalid URL: ${url} What it means
assertFeedUrl wraps new URL in try/catch and throws 'teamtailor: invalid URL' when the constructor rejects the string. This is the first of three sequential checks (parse → https → host); it fires before host validation, so the value is not even syntactically valid. The check runs on the derived /jobs.rss URL as well as any explicit feed URL.
Source
Thrown at providers/teamtailor.mjs:38
// explicit `provider: teamtailor`. Either way the fetch is HTTPS-only with
// `redirect: 'error'`. Job `<link>`s (branded domains) are emitted as-is and
// never fetched.
const TEAMTAILOR_HOST_RE = /^([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\.teamtailor\.com$/i;
/**
* Validate a feed URL before fetching. Always HTTPS-only. The hostname is
* pinned to `*.teamtailor.com` for auto-detected entries; an explicit
* `provider: teamtailor` entry may use its configured branded host.
* @param {string} url
* @param {{ explicit?: boolean }} [opts]
*/
function assertFeedUrl(url, { explicit = false } = {}) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`teamtailor: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`teamtailor: URL must use HTTPS: ${url}`);
if (!explicit && !TEAMTAILOR_HOST_RE.test(parsed.hostname)) {
throw new Error(`teamtailor: untrusted hostname "${parsed.hostname}" — must be <slug>.teamtailor.com (or set "provider: teamtailor" to use a branded careers domain)`);
}
return url;
}
// Derive the RSS feed URL from a tracked_companies entry by normalizing any
// path on the configured host to /jobs.rss. Auto-detection (explicit=false)
// only claims *.teamtailor.com hosts; an explicit `provider: teamtailor` entry
// (explicit=true) may use a branded careers host. Returns null otherwise.
/**
* @param {import('./_types.js').PortalEntry} entry
* @param {{ explicit?: boolean }} [opts]
*/
function resolveFeedUrl(entry, { explicit = false } = {}) {
const raw = entry?.api || entry?.careers_url || '';View on GitHub (pinned to 9b17a8ac97)
Solutions
- Prefix the value with https://
- Quote the scalar in portals.yml if it contains special characters
- Use the full form https://<slug>.teamtailor.com (the provider appends /jobs.rss itself)
Example fix
# before careers_url: acme.teamtailor.com/jobs # after careers_url: https://acme.teamtailor.com
Defensive patterns
Strategy: validation
Validate before calling
function isValidUrl(v) {
if (typeof v !== 'string' || !v) return false;
try { new URL(v); return true; } catch { return false; }
}
if (!isValidUrl(entry.api || entry.careers_url)) {
console.warn(`${entry.name}: teamtailor feed URL is not a valid absolute URL`);
} Type guard
/** @param {unknown} v @returns {v is string} */
function isValidUrl(v) {
if (typeof v !== 'string' || !v) return false;
try { new URL(v); return true; } catch { return false; }
} Prevention
- Always include the https:// scheme in YAML.
- Run a config-lint pass parsing every feed URL.
When it happens
Trigger: The feed URL string cannot be parsed: missing scheme, embedded spaces, or a corrupted value. For auto-detected entries this typically means careers_url was malformed; for explicit provider: teamtailor entries it can be a malformed api/careers_url.
Common situations: YAML typo, a paste that dropped 'https://', or an unquoted scalar that YAML parsed into multiple tokens.
Related errors
- teamtailor: URL must use HTTPS: ${url}
- teamtailor: untrusted hostname "${parsed.hostname}" — must b
- glints: invalid URL: ${url}
- glints: URL must use HTTPS: ${url}
- greenhouse: invalid URL: ${url}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/5722c6a123e4552b.
Report an issue: GitHub.