jackwener/OpenCLI · error · CommandExecutionError
Malformed ChatGPT Deep Research ${label}: expected object so
Error message
Malformed ChatGPT Deep Research ${label}: expected object source row. What it means
Thrown by the addSource helper that normalizes Deep Research citation/source rows. Each row from metadata.safe_urls or metadata.search_result_groups must be a plain object; if a row is null, a primitive (string/number), or an array, the code cannot extract url/title fields and throws this error instead of silently emitting a corrupt source entry.
Source
Thrown at clis/chatgpt/utils.js:1348
try {
return JSON.parse(value);
} catch {
return null;
}
}
function extractDeepResearchSourcesFromReportMessage(reportMessage) {
const metadata = reportMessage?.metadata && typeof reportMessage.metadata === 'object'
? reportMessage.metadata
: {};
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;
}
};View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the raw metadata.search_result_groups / metadata.safe_urls array and drop non-object entries before calling the extraction helper.
- Coerce string URL rows into {url} objects upstream: rows.map(r => typeof r === 'string' ? {url: r} : r).filter(Boolean).
- Pin/vendor the ChatGPT client version matching the current backend payload shape, or update the parser for the new row schema.
- Wrap extraction in try-catch and fall back to rendering the un-normalized sources.
Example fix
// before metadata.search_result_groups.forEach(group => collectSources(group, addSource)); // after const rows = (metadata.search_result_groups || []).filter(s => s && typeof s === 'object' && !Array.isArray(s)); rows.forEach(group => collectSources(group, addSource));
Defensive patterns
Strategy: validation
Validate before calling
const rows = Array.isArray(metadata?.search_result_groups) ? metadata.search_result_groups : [];
const bad = rows.filter(r => !r || typeof r !== 'object' || Array.isArray(r));
if (bad.length) console.warn('dropping non-object source rows', bad); Type guard
const isSourceRow = (s) => !!s && typeof s === 'object' && !Array.isArray(s);
Try / catch
try {
extractDeepResearchSources(metadata);
} catch (err) {
if (String(err.message).includes('expected object source row')) {
metadata.search_result_groups = metadata.search_result_groups.filter(isSourceRow);
return extractDeepResearchSources(metadata);
}
throw err;
} Prevention
- Filter source arrays with a non-array object guard before extraction
- Keep a fixture test mirroring the live Deep Research metadata shape
- Never hand-write source rows in tests without url/href keys
- Log raw metadata when extraction throws to speed up schema-change diagnosis
When it happens
Trigger: metadata.safe_urls or metadata.search_result_groups contains a null, undefined, string, number, or array element passed to addSource (typeof source !== 'object' or source is falsy).
Common situations: ChatGPT backend schema changes to Deep Research annotation payloads; upstream responses mixing string URLs directly into safe_urls instead of objects; partial/JSON-decoded rows where null entries survived filtering.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Malformed ChatGPT conversation payload for Deep Research ext
- Malformed ChatGPT Deep Research ${label}: missing source URL
- Malformed ChatGPT Deep Research ${label}: invalid source URL
- ChatGPT conversation payload id mismatch: expected ${expecte
- INVALID_ARGUMENT
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/c5edba3424b42e43.
Report an issue: GitHub.