Environment details
Steps to reproduce
-
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'
-
Let release-please update the open release pull request - the changelog bullet now contains a literal <details> string inside backticks.
-
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
-
htmlEscape() in src/changelog-notes/default.ts deliberately leaves </> untouched inside inline code spans (so markdown rendering stays correct).
-
The generated release PR body therefore contains a literal `<details>` string.
-
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.
-
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. `<details>` 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.
Environment details
release-please17.6.0 viagoogleapis/release-please-action@v5, manifest mode, single package (nodestrategy)<...>inside inline code spans in commit subjects still break PR-body parsing, silently skipping a component release (manifest mode) #2801 (regression of the former, still open). This report documents a second, harder failure mode of the same underlying gap: raw HTML-looking tokens from commit subjects reach the PR body and are later re-parsed by an HTML parser.Steps to reproduce
Land a commit whose conventional-commit subject contains an inline-code token that is itself an HTML tag name, e.g.
Let release-please update the open release pull request - the changelog bullet now contains a literal
<details>string inside backticks.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:
Full stack trace (captured by invoking
Manifest.createPullRequests()directly against the affected repository):Root cause
htmlEscape()insrc/changelog-notes/default.tsdeliberately leaves</>untouched inside inline code spans (so markdown rendering stays correct).The generated release PR body therefore contains a literal
`<details>`string.On every subsequent run,
Manifest.findOpenReleasePullRequests()re-parses that body.extractMultipleReleases()insrc/util/pull-request-body.tsusesnode-html-parser, which does not understand markdown: the backticked token becomes a real<details>element without a<summary>child.The code then dereferences it unconditionally:
Note the asymmetry:
#2801shows 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
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.