rust-lang/rust · error · anyhow::Error

list continuation unexpectedly terminated

Error message

list continuation unexpectedly terminated

What it means

Returned by the release-notes parser when a `+` list-continuation marker is followed by end-of-input — the parser peek()s for the continued block (source block, listing, title, image, etc.) but the iterator is empty, so there is nothing to continue.

Source

Thrown at src/tools/rust-analyzer/xtask/src/publish/notes.rs:106

        let mut nesting = ListNesting::default();
        while let Some(line) = self.iter.peek() {
            let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;

            if get_list_item(line).is_some() {
                let line = self.iter.next().unwrap()?;
                let line = process_inline_macros(&line)?;
                let (marker, item) = get_list_item(&line).unwrap();
                nesting.set_current(marker);
                self.write_list_item(item, &nesting);
                self.process_paragraph(nesting.indent(), |line| {
                    line.is_empty() || get_list_item(line).is_some() || line == "+"
                })?;
            } else if line == "+" {
                let _ = self.iter.next().unwrap()?;
                let line = self
                    .iter
                    .peek()
                    .ok_or_else(|| anyhow!("list continuation unexpectedly terminated"))?;
                let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;

                let indent = nesting.indent();
                if line.starts_with('[') {
                    self.write_line("", 0);
                    self.process_source_code_block(indent)?;
                } else if line.starts_with(LISTING_DELIMITER) {
                    self.write_line("", 0);
                    self.process_listing_block(None, indent)?;
                } else if line.starts_with('.') {
                    self.write_line("", 0);
                    self.process_block_with_title(indent)?;
                } else if line.starts_with(IMAGE_BLOCK_PREFIX) {
                    self.write_line("", 0);
                    self.process_image_block(None, indent)?;
                } else if line.starts_with(VIDEO_BLOCK_PREFIX) {
                    self.write_line("", 0);
                    self.process_video_block(None, indent)?;

View on GitHub (pinned to 7088e4b63a)

Solutions

  1. Open the notes file and remove the trailing `+` or add the block it was meant to continue.
  2. Run the parser on the file standalone to get the line number, then fix that line.
  3. Add a lint/test that rejects a file ending in a continuation marker.

Example fix

<!-- before: notes.md ends with a dangling continuation -->
* Some bullet
+ <!-- nothing follows; EOF -->

<!-- after: either drop the marker or supply the block -->
* Some bullet
+
----
code block content
----
Defensive patterns

Strategy: validation

Validate before calling

// Reject release notes that end in a dangling continuation before running publish:
fn ends_cleanly(text: &str) -> bool {
    let trimmed = text.trim_end();
    !trimmed.ends_with('\n+') && !trimmed.ends_with('+')
}

Type guard

null

Try / catch

match publish::notes::parse(&notes) {
    Ok(out) => Ok(out),
    Err(e) if e.to_string().contains("list continuation") => {
        eprintln!("notes file has a dangling `+`; remove it or add the block it continues");
        Err(e)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Running the publish/notes xtask on a changelog/release-notes Markdown file where a list item ends with a bare `+` on the last line (no following block). The AsciiDoc-style continuation expects content after it.

Common situations: Editing the release notes and leaving a dangling `+` at the end of the file; a merge that truncated the notes; copy-paste that dropped the block following the continuation marker.

Related errors


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/65e018aa3ba2316b. Report an issue: GitHub.