Skip to content

TypeError: Cannot read properties of undefined (reading 'match') when a commit subject contains a raw <details> token - crashes re-parsing of the open release pull request body #2884

Description

@johnitvn

Environment details

Steps to reproduce

  1. Land a commit whose conventional-commit subject contains an inline-code token that is itself an HTML tag name, e.g.

    git commit -m 'fix: escape an unbalanced `<details>` tag instead of refusing the draft'
    
  2. Let release-please update the open release pull request - the changelog bullet now contains a literal <details> string inside backticks.

  3. On the next push to the default branch, the workflow fails every time until the pull request is merged or its body is edited by hand:

✔ Looking for open release pull requests
##[error]release-please failed: Cannot read properties of undefined (reading 'match')

Full stack trace (captured by invoking Manifest.createPullRequests() directly against the affected repository):

TypeError: Cannot read properties of undefined (reading 'match')
    at extractMultipleReleases (build/src/util/pull-request-body.js:104)
    at PullRequestBody.parse (build/src/util/pull-request-body.js:38)
    at FilePullRequestOverflowHandler.parseOverflow (build/src/util/pull-request-overflow-handler.js:74)
    at Manifest.findOpenReleasePullRequests (build/src/manifest.js:512)

Root cause

  1. htmlEscape() in src/changelog-notes/default.ts deliberately leaves </> untouched inside inline code spans (so markdown rendering stays correct).

  2. The generated release PR body therefore contains a literal `<details>` string.

  3. On every subsequent run, Manifest.findOpenReleasePullRequests() re-parses that body. extractMultipleReleases() in src/util/pull-request-body.ts uses node-html-parser, which does not understand markdown: the backticked token becomes a real <details> element without a <summary> child.

  4. The code then dereferences it unconditionally:

    const summaryNode = detail.getElementsByTagName('summary')[0];
    const summary = summaryNode?.textContent;
    const match = summary.match(SUMMARY_PATTERN); // <- throws when summaryNode is missing

Note the asymmetry: #2801 shows a raw token like <path> silently swallowing subsequent <details> blocks (component skipped); a raw token that is <details> produces a summary-less element and this unguarded .match() turns the same gap into a hard crash that blocks all releases.

Minimal reproduction

const {PullRequestBody} = require('release-please/build/src/util/pull-request-body');
const body = [
  ':robot: I have created a release *beep* *boop*',
  '---',
  '',
  '## [0.9.1](https://github.com/example/repo/compare/v0.9.0...v0.9.1) (2026-08-22)',
  '',
  '### Bug Fixes',
  '',
  '* escape an unbalanced `<details>` tag instead of refusing the draft ([#133](https://github.com/example/repo/issues/133))',
  '',
  '---',
  'footer',
].join('\n');
console.log(PullRequestBody.parse(body));
// => TypeError: Cannot read properties of undefined (reading 'match')
// Same body with `<details>` replaced by e.g. `&lt;details&gt;` parses fine.

Suggested fix

Skip <details> blocks that have no <summary> (warn and continue), so parsing falls back to the single-release path instead of crashing. Real generated blocks always carry a <summary>, so nothing else changes. I have a patch with tests ready and will open a PR referencing this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions