jackwener/OpenCLI · error · CommandExecutionError

${commandLabel} returned an unreadable summary payload

Error message

${commandLabel} returned an unreadable summary payload

What it means

ensureCompleteSummaryRows validates the JSON returned by ESummary before mapping PMIDs to rows. If `result` is missing, not an object, or lacks an object-shaped `result.result`, it throws `${commandLabel} returned an unreadable summary payload`. The library throws this because it cannot safely extract per-PMID summaries from an unexpected response shape.

Source

Thrown at clis/pubmed/utils.js:233

export function summaryToRow(article, rank, pmid = article?.uid) {
    const id = String(pmid ?? article?.uid ?? '').trim();
    return {
        rank,
        pmid: id,
        title: truncateText(String(article?.title ?? '').replace(/\.$/, ''), 120),
        authors: extractAuthors(article?.authors, 3),
        journal: truncateText(article?.fulljournalname || article?.source || '', 60),
        year: String(article?.pubdate ?? '').split(' ')[0] || '',
        article_type: articleTypeFromList(article?.pubtype),
        doi: extractDoi(article?.articleids),
        url: buildPubMedUrl(id),
    };
}

export function ensureCompleteSummaryRows(pmids, result, commandLabel) {
    if (!result || typeof result !== 'object' || !result.result || typeof result.result !== 'object') {
        throw new CommandExecutionError(`${commandLabel} returned an unreadable summary payload`);
    }
    const rows = pmids.map((pmid, index) => {
        const article = result.result[pmid];
        if (!article) {
            return null;
        }
        return summaryToRow(article, index + 1, pmid);
    });
    if (rows.some(row => row === null)) {
        throw new CommandExecutionError(`${commandLabel} omitted summaries for one or more PMIDs`, 'Refusing to return a partial result set.');
    }
    return rows;
}

export function buildSearchQuery(query, filters = {}) {
    const terms = [requireText(query, 'query')];
    if (filters.author) terms.push(`${requireText(filters.author, 'author')}[Author]`);
    if (filters.journal) terms.push(`${requireText(filters.journal, 'journal')}[Journal]`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log/inspect the raw response body to see what NCBI actually returned
  2. Retry the request — transient NCBI issues can produce malformed bodies
  3. Verify the PMIDs and esummary parameters are valid
  4. Check whether NCBI changed the ESummary JSON schema and update the CLI if so

Example fix

// before
const rows = await result(pmids); // throws on unreadable payload
// after
let rows;
try {
  rows = await result(pmids);
} catch (e) {
  console.error('raw payload:', e.detail); // inspect what came back
  throw e;
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate the summary payload before mapping rows
function isReadableSummary(json) {
  return json && typeof json === 'object'
    && json.result && typeof json.result === 'object';
}

Type guard

function hasSummaryResult(json) {
  return typeof json === 'object' && json !== null
    && typeof json.result === 'object' && json.result !== null;
}

Try / catch

try {
  const rows = await result(pmids);
} catch (e) {
  if (/unreadable summary payload/.test(e.message)) {
    console.error('payload detail:', e.detail);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling fetchSummaryRows (the result/esummary command) when NCBI returns an unexpected body for retmode=json — e.g. an error JSON without the usual `result` envelope, a redirect body, or an API format change.

Common situations: NCBI changing the ESummary JSON schema; a proxy returning a different payload; passing invalid web-env/params causing NCBI to return an error envelope instead of summaries; service outage mid-response.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/0b2f131671c1b4f7. Report an issue: GitHub.