santifer/career-ops · error · Error
careerviet: invalid URL: ${url}
Error message
careerviet: invalid URL: ${url} What it means
The CareerViet provider is hard-pinned to a single trusted host, careerviet.vn (TRUSTED_HOST). assertCareerVietUrl first attempts new URL(url); a string that cannot parse as an absolute URL throws this error before any other validation. Unlike bamboohr/breezy which allow tenant subdomains, careerviet accepts only the exact apex host, but URL well-formedness is checked first.
Source
Thrown at providers/careerviet.mjs:106
* The "Cập nhật" (updated) date sits in its own <li>, sibling to "Hạn nộp"
* (deadline) inside the same .time block — the label text is what tells them
* apart; both wrap their <time> the same way.
*/
const UPDATED_DATE_RE = /Cập nhật(?:<!--[\s\S]*?-->)?\s*:?\s*(?:<\/span>)?\s*<time>([\d/-]+)<\/time>/i;
/** @param {any} ctx @param {number} ms */
function sleep(ctx, ms) {
if (typeof ctx?.sleep === 'function') return ctx.sleep(ms);
return new Promise((r) => setTimeout(r, ms));
}
/** @param {string} url */
function assertCareerVietUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`careerviet: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`careerviet: URL must use HTTPS: ${url}`);
if (parsed.hostname !== TRUSTED_HOST) {
throw new Error(`careerviet: untrusted hostname "${parsed.hostname}" — must be ${TRUSTED_HOST}`);
}
return url;
}
/**
* Collapse a markup fragment to its visible text.
* @param {string} fragment
* @returns {string}
*/
export function visibleText(fragment) {
return decodeEntities(
String(fragment ?? '')
.replace(/<!--[\s\S]*?-->/g, ' ')
.replace(/<[^>]+>/g, ' '),View on GitHub (pinned to 1696bec4d0)
Solutions
- Make the value an absolute URL including the scheme: https://careerviet.vn/viec-lam
- Trim whitespace and verify no invisible characters broke the URL string
- Inspect the interpolated URL in the error message — 'undefined' or '' points to an unset variable upstream
- In code, construct URLs via new URL(path, 'https://careerviet.vn') so well-formedness is guaranteed
Example fix
// before careers_url: careerviet.vn/viec-lam // after careers_url: https://careerviet.vn/viec-lam
Defensive patterns
Strategy: validation
Validate before calling
function isParseableUrl(u) {
if (typeof u !== 'string' || !u.trim()) return false;
try { new URL(u); return true; } catch { return false; }
}
if (!isParseableUrl(entry.careers_url)) throw new Error(`bad careerviet URL for ${entry.name}: ${JSON.stringify(entry.careers_url)}`); Type guard
function parseUrlOrNothing(value) {
if (typeof value !== 'string') return null;
try { return new URL(value); } catch { return null; }
} Try / catch
try {
await provider.fetch(entry, ctx);
} catch (err) {
if (String(err.message).includes('invalid URL')) {
console.error(`Unparseable careerviet URL for ${entry.name} — add the https:// scheme and check variables`, err.message);
} else throw err;
} Prevention
- Store absolute https:// URLs in entries — never bare domains or scheme-less strings
- Check the failing string in the error message for 'undefined' or empty values from unset variables
- Build page URLs with new URL(path, 'https://careerviet.vn') so they always parse
- Trim whitespace and strip invisible characters from copy-pasted config values
When it happens
Trigger: Calling the provider or assertCareerVietUrl with a non-URL string: a bare domain like 'careerviet.vn/viec-lam' without the scheme, an empty string, a string with spaces, or an interpolated variable that is undefined (producing 'undefined').
Common situations: YAML entry missing the https:// prefix; template string where the URL variable was never set; copy-paste from a document that stripped the scheme; building the URL from base + path where base itself was malformed.
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
- bamboohr: untrusted hostname "${parsed.hostname}" — must mat
- bamboohr: cannot derive API URL for ${entry.name}
- beesite: cannot resolve search host for ${entry.name}
- breezy: invalid URL: ${url}
- breezy: cannot derive API URL for ${entry.name}
AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01).
Data as JSON: /api/errors/9a43fd1b259276a1.
Report an issue: GitHub.