santifer/career-ops · error · Error
manfred: URL must use HTTPS
Error message
manfred: URL must use HTTPS: ${url} What it means
After parsing succeeds, assertManfredUrl() enforces that the feed is only fetched over HTTPS. Any URL whose protocol is not 'https:' (typically http:, or schemes like ftp:) is rejected with this error. This is a deliberate transport-security pin so credentials and job-seeker data are never sent over plaintext.
Solutions
- Change the URL scheme to https:// in the entry config.
- If you intentionally need plain HTTP (e.g. a local test), use a local HTTPS terminator or mock the fetchJson context rather than downgrading the check.
- Verify no redirect/rewrite step upstream is emitting http:// links into the config.
Example fix
// before const url = 'http://getmanfred.com/api/feed'; // after const url = 'https://getmanfred.com/api/feed';
Defensive patterns
Strategy: validation
Validate before calling
const u = new URL(entry.careers_url);
if (u.protocol !== 'https:') throw new Error(`${entry.name}: careers_url must be https:// (got ${u.protocol})`); Type guard
const isHttpsUrl = (v) => { try { return new URL(v).protocol === 'https:'; } catch { return false; } }; Try / catch
try {
await provider.fetch(entry, ctx);
} catch (err) {
if (String(err.message).startsWith('manfred: URL must use HTTPS')) {
console.error('Fix: change the entry URL scheme to https://');
return;
}
throw err;
} Prevention
- Always write https:// explicitly in config; never copy bare hostnames.
- Add a startup check that rejects non-https URLs in all provider entries.
- Watch for redirects that downgrade to http when testing locally — pin scheme in tests too.
When it happens
Trigger: Configuring a Manfred feed/careers_url with an http:// scheme, e.g. http://getmanfred.com/api/feed, or a URL with an unexpected scheme (ftp:, file:) that still parses as a URL.
Common situations: Copying an http:// link from old docs or a local proxy setup; a config migration that rewrote hosts but not schemes; forgetting that localhost test URLs must also be https or use a different provider path.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- arbeitnow: URL must use HTTPS
- eightfold: URL must use HTTPS
- 4dayweek: URL must use HTTPS
- ashby: URL must use HTTPS
- bamboohr: URL must use HTTPS
AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16).
Data as JSON: /api/errors/6dfbfadfa0fbec74.
Report an issue: GitHub.
Appendix: source
Thrown at providers/manfred.mjs:43
// The full catalogue in one response runs ~2.3MB and takes 8-9.6s to arrive
// even on a clean connection — right against the shared 10s default timeout,
// so any network jitter aborts it. Give it real headroom rather than relying
// on retry alone to paper over a structurally near-timeout request.
const FETCH_TIMEOUT_MS = 25_000;
const TRUSTED_HOST = 'www.getmanfred.com';
const OFFER_BASE = 'https://www.getmanfred.com/ofertas-empleo';
const VALID_LANGS = ['EN', 'ES'];
const DEFAULT_LANG = 'EN';
/** @param {string} url */
function assertManfredUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`manfred: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`manfred: URL must use HTTPS: ${url}`);
if (parsed.hostname !== TRUSTED_HOST) {
throw new Error(`manfred: untrusted hostname "${parsed.hostname}" — must be ${TRUSTED_HOST}`);
}
return url;
}
/** Resolve the feed language: `lang` on the entry, uppercased, else EN. */
export function resolveLang(entry) {
const raw = typeof entry?.lang === 'string' ? entry.lang.trim().toUpperCase() : '';
return VALID_LANGS.includes(raw) ? raw : DEFAULT_LANG;
}
// The feed reports currency as the SYMBOL, not an ISO code, and the observed
// values include a narrow-no-break-space variant of the euro sign. scan.mjs's
// salary_filter compares currencies case-insensitively as plain strings, so a
// symbol would never match a user's `currency: EUR` — map to ISO, and drop the
// field entirely rather than guess when the symbol is unknown.
const CURRENCY_BY_SYMBOL = new Map([View on GitHub (pinned to e7abd431fc)