[Bug] DOCX table cells with manual line breaks still split Markdown tables in knowledge-base extraction
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- python
- Domain
- data-engineering
Research direction
Start with api/core/rag/extractor/word_extractor.py, especially WordExtractor._parse_cell() and _table_to_markdown(). Run the DOCX reproduction described in the issue or the regression test mentioned there, checking that both header and body cell breaks stay within one Markdown table row. Done means the extracted table has three lines and represents the cell break as <br>.
Written by the indexing model from the issue text.
Description
Self Checks
- I have read the Contributing Guide and Language Policy.
- This is only for a bug report; questions belong in Discussions.
- I have searched existing issues and pull requests, including closed ones.
- I confirm that I am using English to submit this report.
- I have not modified the issue template and have filled in the required fields.
Dify version
main at 507313ada516e75f748313ccf7626e04d40bc250 (API 1.17.1)
Cloud or Self Hosted
Self Hosted (Source)
Steps to reproduce
A DOCX table cell can contain a manual line break (Shift+Enter). python-docx exposes that break as a \n inside the run text. Build a minimal table:
from docx import Document
doc = Document()
table = doc.add_table(rows=2, cols=2)
table.cell(0, 0).text = "Name"
table.cell(0, 1).text = "Notes"
table.cell(1, 0).text = "A"
p = table.cell(1, 1).paragraphs[0]
r = p.add_run("line1")
r.add_break()
p.add_run("line2")
doc.save("table-with-breaks.docx")
Then run WordExtractor._table_to_markdown() on the saved table, or upload the file to a knowledge base and inspect the extracted chunk.
✔️ Expected Behavior
The cell keeps its line break without adding a new Markdown table row:
| Name | Notes |
| --- | --- |
| A | line1<br>line2 |
❌ Actual Behavior
WordExtractor._parse_cell() returns the raw \n, and _table_to_markdown() emits it unchanged. The Markdown row is split:
'| Name | Notes |\n| --- | --- |\n| A | line1\nline2 |'
splitlines() therefore returns four lines instead of the three lines the table actually contains. The table no longer describes the same columns row by row, and the corrupted structure is what gets chunked, embedded, and returned as knowledge context.
Root cause
api/core/rag/extractor/word_extractor.py:
_table_to_markdown()joins cells with" | "and later joins rows with"\n"._parse_cell()is the shared source for header and body cells and currently ends with" ".join(unique_content), with no normalization of line endings.
A raw newline has no valid representation inside a Markdown table cell; GFM uses <br> for an intra-cell line break.
History
This was reported before as #12436 and closed as completed after #12112 merged. That fix only changed api/core/workflow/nodes/document_extractor/node.py, replacing cell.text.replace("\n", "<br>") in the old workflow document node. That file no longer exists on main, and the knowledge-base path uses WordExtractor, so the original behavior is still reproducible. This report is about the remaining WordExtractor path, not a duplicate of the pipe-escaping issue in #42358.
Proposed fix
Normalize cell line endings in WordExtractor._parse_cell() before returning, using the existing <br> convention:
cell_text = " ".join(unique_content)
return cell_text.replace("\r\n", "\n").replace("\r", "\n").replace("\n", "<br>")
I verified this with a real DOCX regression test covering both a header cell and a body cell. The test fails on the current base with extra output lines and passes with the change. A PR will follow and link this issue.
Prepared with AI assistance; the reproduction, code-path analysis, and test results were verified locally.
- Dominant language
- Python
- Stars
- 160k
- Forks
- 25.3k
- Avg merge
- 21h 9m
- Merged PRs (30d)
- 614
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from langgenius/dify
-
Custom tool sends a form-encoded body when the request body content type is application/json; charset=utf-8Possibly taken @Revinand claimed this 1 day ago. Open1.18.0
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
langgenius/dify#44907 · 1 reaction ·
Maintainers usually reply within 1 day
-
PHP workflow streaming requests buffer the full SSE responsePossibly taken @Winoren claimed this 11 days ago. Openreview: medium
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
langgenius/dify#44270 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
-
Node.js SDK rejects attachment-only hit testing with an empty queryPossibly taken @Winoren claimed this 11 days ago. Openreview: medium
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
langgenius/dify#44266 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
-
Node.js SDK rejects empty first-page pagination cursorsPossibly taken @Winoren claimed this 11 days ago. Openreview: medium
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
langgenius/dify#44264 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
-
CSVExtractor rejects explicit on_bad_lines parsing options with duplicate keywordsPossibly taken @lorenzozanee claimed this 24 days ago. Openreview: medium
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
langgenius/dify#43475 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Maintainers usually reply within 1 day
-
[oblt-aw][autodoc] Document obs-aw-automerge contract and fix automerge doc inconsistenciesPossibly taken @github-actions claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 1 day
-
admission-deferred priority: medium status: ready work-class: scoped
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
melodic-software/claude-code-plugins#10887 · 4 comments ·
Maintainers usually reply within 1 day
-
backend bug good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
PoCInnovation/Distribox#255 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
google/langextract#598 ·
Maintainers usually reply within 1 day