Skip to content

fix(email): fall back to the HTML body when the plain part is blank - #4295

Merged
dolfim-ibm merged 1 commit into
docling-project:mainfrom
chrikrah:fix/email-blank-plain-part
Sep 21, 2026
Merged

dolfim-ibm merged 1 commit into
docling-project:mainfrom
chrikrah:fix/email-blank-plain-part

Conversation

@chrikrah

Copy link
Copy Markdown
Contributor

_get_body_paragraphs checks whether a part is present, not whether it produced text, then returns unconditionally, at docling/backend/email_backend.py:270:

if self.mail.text_plain:
    paragraphs: list[str] = []
    for part in self.mail.text_plain:
        paragraphs.extend(self._split_paragraphs(part))
    return paragraphs

A multipart/alternative message can carry a whitespace-only text/plain part beside a real text/html one, and many senders generate exactly that. mail.text_plain is then a truthy [' '], _split_paragraphs(" ") gives [], and the branch returns that empty list instead of falling through to the HTML one.

The whole body is dropped, and the conversion still reports success.

Before

A two-part message whose plain part is a single space, converted through DocumentConverter.convert():

# Blank Plain Part

From: Alice <[email protected]>

To: Bob <[email protected]>

After

The same message, with the body present:

# Blank Plain Part

From: Alice <[email protected]>

To: Bob <[email protected]>

Real content that should appear.

Each branch now returns only once it has produced a paragraph. The final self.mail.body fallback stays as it was.

Tests

$ python -m pytest tests/test_backend_email.py -q
23 passed

$ python -m ruff check docling/backend/email_backend.py tests/test_backend_email.py
All checks passed!
$ python -m ruff format --check docling/backend/email_backend.py tests/test_backend_email.py
2 files already formatted

One new case builds the message as raw bytes and converts it through DocumentConverter.convert(), the path a user takes.

Revert email_backend.py alone, keep the case, and the file gives 1 failed, 22 passed.

No groundtruth changed. No committed .eml fixture carries a blank alternative part, so the suite never reached this line.

Scope

A message whose plain part holds real text behaves as before: the branch still returns on the first paragraph it finds. Only the case where a branch produced nothing changes.

Duplicate search

Two open pull requests touch docling/backend/email_backend.py: #3978 and #3791. Neither mentions _get_body_paragraphs or text_plain.

Closed #4259 touched this file for a different concern, adding isinstance(part, str) guards against non-string parts. Its diff leaves this control flow alone, so a blank string part still reproduces on top of it.

Checklist:

  • Documentation has been updated, if necessary.
    No document covers which part supplies the body.
  • Examples have been added, if necessary.
    The change corrects existing output rather than adding a capability.
  • Tests have been added, if necessary.

_get_body_paragraphs checks whether a part is present, not whether it produced
text, then returns unconditionally:

    if self.mail.text_plain:
        ...
        return paragraphs

A multipart/alternative message can carry a whitespace-only text/plain part
beside a real text/html one, which many senders generate automatically. That
makes mail.text_plain a truthy [' '], _split_paragraphs returns [], and the
empty list is returned instead of falling through to the HTML body. The whole
message renders with no body.

Before, on a two-part message whose plain part is a single space:

    # Blank Plain Part
    From: Alice <[email protected]>
    To: Bob <[email protected]>

After, the same message also carries 'Real content that should appear.'

Tests: 23 passed in tests/test_backend_email.py. One new case builds the
message as raw bytes and converts it through DocumentConverter.convert().
Reverting email_backend.py alone gives 1 failed, 22 passed.

ruff check and ruff format --check are clean on both files.

Signed-off-by: Christopher Krah <[email protected]>
@github-actions

Copy link
Copy Markdown
Contributor

✅ DCO Check Passed

Thanks @chrikrah, all your commits are properly signed off. 🎉

@mergify

mergify Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@dolfim-ibm dolfim-ibm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@codecov

codecov Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
docling/backend/email_backend.py 75.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@dolfim-ibm
dolfim-ibm merged commit 8e37971 into docling-project:main Sep 21, 2026
26 checks passed
@chrikrah
chrikrah deleted the fix/email-blank-plain-part branch October 20, 2026 00:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants