santifer/career-ops · error
collage: invalid URL
Error message
collage: invalid URL: ${url} What it means
collage provider's assertCollageApiUrl validates an explicitly configured API URL and the first check is that `new URL(url)` parses at all. This error means the string supplied as the entry's `api` field is not a valid absolute URL (missing scheme, whitespace-only, garbage). It is an input-validation guard so a malformed portals.yml entry fails loudly instead of producing a nonsense fetch.
Solutions
- Check the `api:` value in portals.yml and add the full absolute URL including `https://`
- Ensure there are no stray spaces, newlines, or invisible characters inside the quoted value
- Use the canonical form: https://api.collage.co/v1/positions/<job-site-address>
- If you only have a secure.collage.co careers page URL, put it in `careers_url:` instead and let the provider derive the API URL
Example fix
# before (portals.yml) api: api.collage.co/v1/positions/acme # after api: https://api.collage.co/v1/positions/acme
Defensive patterns
Strategy: validation
Validate before calling
// Validate the portals.yml Collage api value before loading the provider
function isValidCollageApiUrl(url) {
try { return new URL(url).href === url.trim() || new URL(url).protocol === 'https:'; }
catch { return false; }
}
// Pre-check: new URL(entry.api) must not throw; must be absolute with a scheme
Type guard
function isAbsoluteUrlString(v) {
if (typeof v !== 'string' || !v.trim()) return false;
try { new URL(v); return true; } catch { return false; }
} Try / catch
try {
const jobs = await collageProvider.fetch(entry, ctx);
} catch (err) {
if (String(err.message).startsWith('collage: invalid URL')) {
console.error(`Fix portals.yml entry "${entry.name}": api must be an absolute https URL, got "${entry.api}"`);
} else { throw err; }
} Prevention
- Always write full absolute URLs (scheme + host + path) in portals.yml api/careers_url fields
- Quote URL values in YAML so special characters aren't folded or truncated
- Lint portals.yml entries with a URL-format check before running scans
- Prefer providing careers_url and letting the provider construct the API URL
When it happens
Trigger: A portals.yml Collage entry sets `api:` to a string that the URL constructor rejects — e.g. `api.collage.co/v1/positions/foo` without `https://`, a relative path like `/v1/positions/foo`, a value with stray spaces/newlines beyond trim, or a non-string coerced to an empty/mangled string.
Common situations: Hand-editing portals.yml and forgetting the scheme; copying a path from docs without the origin; YAML folding a long URL with an illegal character; typos like `https:/api.collage.co` (single slash).
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
- a16z-speedrun-talent: invalid URL
- agentic-jobs: invalid URL
- collage: API URL must be /v1/positions/<job-site-address>
- eightfold: cannot derive API URL for
- eightfold: untrusted hostname
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/4db79d896dce44e4.
Report an issue: GitHub.
Appendix: source
Thrown at providers/collage.mjs:16
// @ts-check
/** @typedef {import('./_types.js').Provider} Provider */
// Collage HR public job-site API. A job-site address is an explicit tenant
// identifier, not a company-name slug we should guess. Entries may provide
// the exact API URL or a public Collage careers URL from which the final path
// segment is read.
const API_ORIGIN = 'https://api.collage.co';
const COLLAGE_API_HOST = 'api.collage.co';
const COLLAGE_SITE_HOST_RE = /^secure\.collage\.co$/;
/** @param {string} url */
function assertCollageApiUrl(url) {
let parsed;
try { parsed = new URL(url); } catch { throw new Error(`collage: invalid URL: ${url}`); }
if (parsed.protocol !== 'https:') throw new Error(`collage: URL must use HTTPS: ${url}`);
if (parsed.hostname !== COLLAGE_API_HOST) {
throw new Error(`collage: untrusted hostname "${parsed.hostname}" — must be ${COLLAGE_API_HOST}`);
}
if (!/^\/v1\/positions\/[^/?#]+$/.test(parsed.pathname)) {
throw new Error(`collage: API URL must be /v1/positions/<job-site-address>: ${url}`);
}
return url;
}
/** @param {import('./_types.js').PortalEntry} entry */
function resolveApiUrl(entry) {
const explicit = typeof entry.api === 'string' ? entry.api.trim() : '';
if (explicit) return assertCollageApiUrl(explicit);
const raw = typeof entry.careers_url === 'string' ? entry.careers_url.trim() : '';
if (!raw) return null;
let parsed;View on GitHub (pinned to aac998c7ed)