jackwener/OpenCLI · error · CommandExecutionError
Malformed ChatGPT Deep Research ${label}: invalid source URL
Error message
Malformed ChatGPT Deep Research ${label}: invalid source URL. What it means
addSource requires source URLs to be absolute http(s) URLs. After extracting rawUrl, if it does not match /^https?:\/\//i the row is rejected with this error. This guards against protocol-relative, relative, or non-HTTP schemes (ftp:, javascript:, internal paths) being surfaced as citations.
Source
Thrown at clis/chatgpt/utils.js:1359
const references = Array.isArray(metadata.content_references) ? metadata.content_references : [];
const safeUrls = Array.isArray(metadata.safe_urls) ? metadata.safe_urls : [];
const groups = Array.isArray(metadata.search_result_groups) ? metadata.search_result_groups : [];
const byUrl = new Map();
const addSource = (source = {}, label = 'source') => {
if (!source || typeof source !== 'object') {
throw new CommandExecutionError(`Malformed ChatGPT Deep Research ${label}: expected object source row.`);
}
const rawUrl = String(source.url || source.href || source.safe_url || '').trim();
const title = String(source.title || source.name || source.text || '').trim();
if (!rawUrl) {
if (title || source.matched_text || source.metadata) {
throw new CommandExecutionError(`Malformed ChatGPT Deep Research ${label}: missing source URL.`);
}
return;
}
if (!/^https?:\/\//i.test(rawUrl)) {
throw new CommandExecutionError(`Malformed ChatGPT Deep Research ${label}: invalid source URL.`);
}
if (!byUrl.has(rawUrl)) {
byUrl.set(rawUrl, { title, url: rawUrl });
} else if (title && !byUrl.get(rawUrl).title) {
byUrl.get(rawUrl).title = title;
}
};
for (const reference of references) {
const hasDirectSource = reference && typeof reference === 'object'
&& (reference.url || reference.href || reference.safe_url || reference.title || reference.name || reference.text || reference.matched_text);
if (hasDirectSource) addSource(reference, 'content reference');
if (reference?.matched_text) addSource({ title: reference.matched_text, url: reference.url }, 'matched content reference');
if (reference?.metadata) addSource(reference.metadata, 'content reference metadata');
}
for (const url of safeUrls) addSource(typeof url === 'string' ? { url } : url, 'safe URL');
for (const group of groups) {
for (const entry of [View on GitHub (pinned to 49907e53dc)
Solutions
- Normalize the URL before extraction: prefix 'https:' for protocol-relative values or resolve relative URLs against a base origin.
- Pre-validate rows with /^https?:\/\//i and drop or repair non-conforming entries.
- Fix the upstream producer to always emit absolute http(s) URLs.
- Catch and skip the row, logging it as a dropped citation.
Example fix
// before
addSource({ title: 'Doc', url: '/docs/page' });
// after
const u = '/docs/page';
addSource({ title: 'Doc', url: u.startsWith('//') ? 'https:' + u : new URL(u, 'https://example.com').href }); Defensive patterns
Strategy: validation
Validate before calling
const ABS_URL = /^https?:\/\//i;
rows.forEach(r => {
const u = String(r?.url || r?.href || r?.safe_url || '').trim();
if (u && !ABS_URL.test(u)) console.warn('non-absolute source URL', u);
}); Type guard
const isHttpUrl = (s) => typeof s === 'string' && /^https?:\/\//i.test(s.trim());
Try / catch
try {
extractDeepResearchSources(metadata);
} catch (err) {
if (String(err.message).includes('invalid source URL')) {
console.warn('dropping source with bad URL:', err.message);
return { sources: [], degraded: true };
}
throw err;
} Prevention
- Resolve relative URLs against a base origin before extraction
- Upgrade protocol-relative ('//host') URLs to 'https:'
- Reject or repair bare-domain strings by prepending 'https://'
- Validate upstream citation producers emit absolute http(s) URLs only
When it happens
Trigger: A source row whose url/href/safe_url holds a relative path ('/search?q=x'), a protocol-relative URL ('//example.com'), a non-http scheme ('ftp://...', 'file://...'), or a malformed value like 'example.com'.
Common situations: Backend emitting relative CDN or internal links; scraped/citation data using bare domains without scheme; misconfigured proxies rewriting absolute URLs to root-relative ones.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Malformed ChatGPT Deep Research ${label}: expected object so
- Malformed ChatGPT Deep Research ${label}: missing source URL
- Malformed ChatGPT conversation payload for Deep Research ext
- ChatGPT conversation payload id mismatch: expected ${expecte
- INVALID_ARGUMENT
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/63a5d1f4a4b2d3e5.
Report an issue: GitHub.