santifer/career-ops · error
gem: invalid URL
Error message
gem: invalid URL: ${url} What it means
assertGemUrl validates a URL before Gem provider requests. When new URL(url) cannot parse the value (throwing SyntaxError internally), the library wraps it in this domain-specific error. It exists so callers get a clear provider-prefixed message instead of a raw URL parser exception.
Solutions
- Log the exact url value at the throw site and fix the unparseable portion (missing scheme, spaces, stray quotes).
- Trim whitespace and strip surrounding quotes from the URL before passing it.
- Ensure the config entry's URL field is populated — check portals.yml / the board entry for an empty or partially filled value.
- Pre-validate with new URL(url) in a try/catch at load time to fail fast with context.
Example fix
// before
assertGemUrl(process.env.GEM_BOARD_URL || '');
// after
const raw = (process.env.GEM_BOARD_URL || '').trim().replace(/^['"]|['"]$/g, '');
if (!raw) throw new Error('GEM_BOARD_URL is not set');
assertGemUrl(raw); Defensive patterns
Strategy: validation
Validate before calling
function isParseableUrl(u) { try { new URL(u); return true; } catch { return false; } }
if (!isParseableUrl(cfg.url)) throw new Error(`bad Gem URL in config: ${JSON.stringify(cfg.url)}`); Type guard
const isNonEmptyString = (v) => typeof v === 'string' && v.trim().length > 0;
Try / catch
try { assertGemUrl(url); } catch (e) { if (String(e.message).includes('invalid URL')) console.error('Check the URL for missing scheme/spaces:', url); throw e; } Prevention
- Trim and de-quote URLs read from env/config before use
- Validate all board URLs at config load time, not at fetch time
- Always include the scheme (https://) in config values
- Add a startup lint that runs assertGemUrl on every entry
When it happens
Trigger: Passing a URL string that is not parseable by the WHATWG URL parser to assertGemUrl or any code path that resolves a Gem board entry's api/board URL — e.g. an empty string, a value with spaces like 'https://example.com/my board', a malformed scheme ('htp://...'), or a config field that was never populated and interpolated as ''.
Common situations: Typo in the careers_url or api field in portals.yml; an environment-variable-substituted URL that resolved to empty; copy-pasting a URL with surrounding whitespace/quotes; hand-editing board entries and dropping the scheme.
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
- arbeitnow: invalid URL
- ashby: invalid URL
- bamboohr: invalid URL
- breezy: invalid URL
- builtin: invalid URL
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/d92c596ba9b88dae.
Report an issue: GitHub.
Appendix: source
Thrown at providers/gem.mjs:123
// to before this field list was widened.
/** @param {any} posting */
function buildJobDescriptionText(posting) {
const intro = htmlToText(posting?.jobPostSectionHtml?.introHtml);
const body = htmlToText(posting?.descriptionHtml);
const outro = htmlToText(posting?.jobPostSectionHtml?.outroHtml);
const compensation = htmlToText(posting?.compensationHtml);
const text = [intro, body, outro].filter(Boolean).join('\n\n');
return compensation ? [text, `Compensation: ${compensation}`].filter(Boolean).join('\n\n') : text;
}
/** @param {string} url */
function assertGemUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`gem: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`gem: URL must use HTTPS: ${url}`);
if (!ALLOWED_GEM_HOSTS.has(parsed.hostname))
throw new Error(`gem: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_GEM_HOSTS].join(', ')}`);
return url;
}
/** Resolve an explicitly pinned URL for Gem's documented REST job-board API. */
function resolveRestApiUrl(entry) {
const raw = typeof entry.api === 'string' ? entry.api : '';
if (!raw) return null;
let parsed;
try {
parsed = new URL(raw);
} catch {
return null;
}
if (parsed.protocol !== 'https:' || parsed.hostname !== 'api.gem.com') return null;View on GitHub (pinned to aac998c7ed)