santifer/career-ops · error · Error
Unsupported profile photo URL scheme: ${photo.split(':', 1)[
Error message
Unsupported profile photo URL scheme: ${photo.split(':', 1)[0]} What it means
Validation in `prepareCandidatePhoto`: when `photo` has an `other-scheme:` prefix (matches `/^[a-z][a-z0-9+.-]+:/i` but is not `http:`/`https:`, and is not a `data:` URL), the scheme is unsupported and the function throws with the extracted scheme name. Only `http(s)` remote URLs and local file paths are accepted beyond `data:`.
Source
Thrown at build-cv-html.mjs:136
c.photo = '';
return c;
}
if (photo.startsWith('data:')) {
if (!IMAGE_DATA_URL_RE.test(photo)) {
throw new Error('Unsupported profile photo data URL (expected base64 PNG, JPEG, WebP, or GIF)');
}
c.photo = photo;
return c;
}
if (/^https?:\/\//i.test(photo)) {
c.photo = photo;
return c;
}
if (/^[a-z][a-z0-9+.-]+:/i.test(photo)) {
throw new Error(`Unsupported profile photo URL scheme: ${photo.split(':', 1)[0]}`);
}
const photoPath = isAbsolute(photo) ? photo : resolve(__dirname, photo);
const mime = PHOTO_MIME_BY_EXT.get(extname(photoPath).toLowerCase());
if (!mime) {
throw new Error(`Unsupported profile photo format: ${photo} (expected PNG, JPEG, WebP, or GIF)`);
}
let bytes;
try {
bytes = await readFile(photoPath);
} catch (err) {
throw new Error(`Profile photo not found or unreadable: ${photo} (${err.code || err.message})`);
}
if (bytes.length === 0) {
throw new Error(`Profile photo is empty: ${photo}`);
}
c.photo = `data:${mime};base64,${bytes.toString('base64')}`;View on GitHub (pinned to 9b17a8ac97)
Solutions
- Use an `http://` or `https://` URL for remote photos, or a local filesystem path (absolute or relative to the script) for local files.
- For `blob:` URLs, fetch the bytes client-side and pass the resulting `data:` URL or save to a file first.
- For `file://` paths, strip the scheme and pass the raw path instead.
- For IPFS/other protocols, resolve to an https gateway URL or download the file locally.
- Never pass user-controlled `javascript:` or unknown schemes — the rejection is a safety feature.
Example fix
// before photo: 'file:///home/me/avatar.png' // throws 'Unsupported profile photo URL scheme: file' // after photo: '/home/me/avatar.png' // raw absolute path — function resolves and embeds
Defensive patterns
Strategy: validation
Validate before calling
function normalizePhotoSource(p) {
const s = String(p ?? '').trim();
if (/^data:/i.test(s) || /^https?:\/\//i.test(s) || !/^[a-z][a-z0-9+.-]*:/i.test(s)) return s;
throw new Error(`Unsupported photo URL scheme: ${s.split(':', 1)[0]}. Use http(s) or a file path.`);
} Type guard
function isAcceptablePhotoSource(p) {
const s = String(p ?? '').trim();
if (/^data:/i.test(s) || /^https?:\/\//i.test(s)) return true;
// local path: no scheme at all
return !/^[a-z][a-z0-9+.-]*:/i.test(s);
} Prevention
- Use http(s) URLs for remote photos or raw filesystem paths for local ones.
- Resolve blob:/file:// URLs to bytes (data URL or saved file) upstream.
- Treat rejection of javascript:/unknown schemes as a safety feature, never bypass it.
- Test that ftp:, file:, blob: are rejected.
When it happens
Trigger: Passing `photo: 'ftp://example.com/me.png'`, `'file:///Users/me/photo.png'`, `'ipfs://Qm...'`, `'blob:https://...'`, `'javascript:...'`, or any non-http URL-prefixed string.
Common situations: Browser `blob:` URL captured from a webcam UI; `file://` from a desktop uploader; IPFS/decentralized storage URL; copy-pasting an FTP link; security-sensitive schemes like `javascript:` (rejected for safety, not just style).
Related errors
- Unsupported profile photo data URL (expected base64 PNG, JPE
- Unsupported profile photo style: ${style} (expected rounded,
- Unsupported profile photo format: ${photo} (expected PNG, JP
- Access denied: Egress guard blocked private target IP ${ip}
- apify: invalid actorId ${JSON.stringify(actorId)}. Expected
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/a59aca3c896a4b32.
Report an issue: GitHub.