jackwener/OpenCLI · error · CommandExecutionError

pubmed article did not include a title

Error message

pubmed article ${pmid} did not include a title

What it means

CommandExecutionError thrown at clis/pubmed/article.js:31 when the EFetch XML parsed into an article object but the article has no title field. This guards the downstream output which requires a title row; it indicates the NCBI response shape differed from what parseArticleXml expects (or the record genuinely has no title element in the abstract rettype).

Solutions

  1. Dump the raw EFetch XML for this PMID and check whether <ArticleTitle> is present
  2. Update parseArticleXml to the current PubMed XML DTD (e.g. handle namespaced elements or <BookTitle>)
  3. Try a different PMID to determine if the issue is record-specific or a general parser regression
  4. If the record legitimately has no title, surface it gracefully instead of throwing, or instruct users to use a related record
  5. Add a unit test with the failing XML fixture so parser changes are caught in CI

Example fix

// before
const article = parseArticleXml(xml, pmid);
if (!article) {
    throw new EmptyResultError('pubmed article', `No article found for PMID ${pmid}.`);
}
// after
const article = parseArticleXml(xml, pmid);
if (!article) {
    throw new EmptyResultError('pubmed article', `No article found for PMID ${pmid}.`);
}
article.title = article.title || article.bookTitle || article.collectionTitle || '[untitled]';
Defensive patterns

Strategy: try-catch

Type guard

function hasTitle(article) {
  return article != null && typeof article.title === 'string' && article.title.length > 0;
}

Try / catch

try {
  const article = await runCli('pubmed article', { pmid });
} catch (e) {
  if (/did not include a title/.test(e.message)) {
    console.warn(`PMID ${pmid} returned an article without a title; fetching via esummary instead.`);
    return fallbackSummary(pmid);
  }
  throw e;
}

Prevention

When it happens

Trigger: parseArticleXml returned an object without a truthy title because: (a) the <ArticleTitle> element is missing or renamed in the XML, (b) the record is a book/document whose XML uses a different title element, (c) parseArticleXml's title extraction path (xpath/key) no longer matches the returned retmode/rettype XML.

Common situations: PubMed DTD update changes ArticleTitle casing/namespace; fetching an in-process or PubMed-Central-only record that lacks an ArticleTitle; a bug in parseArticleXml after a refactor; requesting rettype 'abstract' for a record type that returns structured XML without titles.

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.

Related errors


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

Appendix: source

Thrown at clis/pubmed/article.js:31

    browser: false,
    defaultFormat: 'plain',
    args: [
        { name: 'pmid', positional: true, required: true, help: 'PubMed ID, e.g. 37780221' },
        { name: 'full-abstract', type: 'boolean', default: false, help: 'Do not truncate the abstract in table output' },
    ],
    columns: ['pmid', 'title', 'authors', 'journal', 'year', 'date', 'article_type', 'language', 'doi', 'pmc', 'affiliations', 'grants', 'mesh_terms', 'keywords', 'abstract', 'url'],
    func: async (args) => {
        const pmid = requirePmid(args.pmid);
        const xml = await eutilsFetch('efetch', {
            id: pmid,
            rettype: 'abstract',
        }, { retmode: 'xml', label: 'pubmed article' });
        const article = parseArticleXml(xml, pmid);
        if (!article) {
            throw new EmptyResultError('pubmed article', `No article found for PMID ${pmid}.`);
        }
        if (!article.title) {
            throw new CommandExecutionError(`pubmed article ${pmid} did not include a title`, 'PubMed EFetch response shape may have changed.');
        }
        const abstract = args['full-abstract'] ? article.abstract : truncateText(article.abstract, 500);
        return [
            {
                pmid: article.pmid,
                title: article.title,
                authors: article.authors.join(', '),
                journal: article.journal,
                year: article.year,
                date: article.date || null,
                article_type: article.article_type,
                language: article.language || null,
                doi: article.doi || null,
                pmc: article.pmc || null,
                affiliations: article.affiliations || null,
                grants: article.grants || null,
                mesh_terms: article.mesh_terms || null,
                keywords: article.keywords || null,

View on GitHub (pinned to 49907e53dc)