{"record":{"id":"cc84c914d93ae3f2","repo":"jackwener/OpenCLI","slug":"pubmed-article-pmid-did-not-include-a-title","errorCode":null,"errorMessage":"pubmed article ${pmid} did not include a title","messagePattern":"pubmed article (.+?) did not include a title","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/pubmed/article.js","lineNumber":31,"sourceCode":"    browser: false,\n    defaultFormat: 'plain',\n    args: [\n        { name: 'pmid', positional: true, required: true, help: 'PubMed ID, e.g. 37780221' },\n        { name: 'full-abstract', type: 'boolean', default: false, help: 'Do not truncate the abstract in table output' },\n    ],\n    columns: ['pmid', 'title', 'authors', 'journal', 'year', 'date', 'article_type', 'language', 'doi', 'pmc', 'affiliations', 'grants', 'mesh_terms', 'keywords', 'abstract', 'url'],\n    func: async (args) => {\n        const pmid = requirePmid(args.pmid);\n        const xml = await eutilsFetch('efetch', {\n            id: pmid,\n            rettype: 'abstract',\n        }, { retmode: 'xml', label: 'pubmed article' });\n        const article = parseArticleXml(xml, pmid);\n        if (!article) {\n            throw new EmptyResultError('pubmed article', `No article found for PMID ${pmid}.`);\n        }\n        if (!article.title) {\n            throw new CommandExecutionError(`pubmed article ${pmid} did not include a title`, 'PubMed EFetch response shape may have changed.');\n        }\n        const abstract = args['full-abstract'] ? article.abstract : truncateText(article.abstract, 500);\n        return [\n            {\n                pmid: article.pmid,\n                title: article.title,\n                authors: article.authors.join(', '),\n                journal: article.journal,\n                year: article.year,\n                date: article.date || null,\n                article_type: article.article_type,\n                language: article.language || null,\n                doi: article.doi || null,\n                pmc: article.pmc || null,\n                affiliations: article.affiliations || null,\n                grants: article.grants || null,\n                mesh_terms: article.mesh_terms || null,\n                keywords: article.keywords || null,","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/pubmed/article.js#L13-L49","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Dump the raw EFetch XML for this PMID and check whether <ArticleTitle> is present","Update parseArticleXml to the current PubMed XML DTD (e.g. handle namespaced elements or <BookTitle>)","Try a different PMID to determine if the issue is record-specific or a general parser regression","If the record legitimately has no title, surface it gracefully instead of throwing, or instruct users to use a related record","Add a unit test with the failing XML fixture so parser changes are caught in CI"],"exampleFix":"// before\nconst article = parseArticleXml(xml, pmid);\nif (!article) {\n    throw new EmptyResultError('pubmed article', `No article found for PMID ${pmid}.`);\n}\n// after\nconst article = parseArticleXml(xml, pmid);\nif (!article) {\n    throw new EmptyResultError('pubmed article', `No article found for PMID ${pmid}.`);\n}\narticle.title = article.title || article.bookTitle || article.collectionTitle || '[untitled]';","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function hasTitle(article) {\n  return article != null && typeof article.title === 'string' && article.title.length > 0;\n}","tryCatchPattern":"try {\n  const article = await runCli('pubmed article', { pmid });\n} catch (e) {\n  if (/did not include a title/.test(e.message)) {\n    console.warn(`PMID ${pmid} returned an article without a title; fetching via esummary instead.`);\n    return fallbackSummary(pmid);\n  }\n  throw e;\n}","preventionTips":["Add XML fixtures from live EFetch responses to parser unit tests","Handle alternative title elements (BookTitle, CollectionTitle) in parseArticleXml","Track PubMed DTD updates in E-Utils release notes","Fall back to esummary for records missing ArticleTitle"],"tags":["api","pubmed","schema-change","parsing"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}