Parser reliability
Markdown Table Parser Edge Cases and Repair Guide
Prepared and reviewed by the md2text maintainer. Parser-specific claims are checked against the public compatibility lab and automated regression suite.
Markdown tables become difficult to convert when the same character can mean two different things. A pipe may separate columns, appear literally in a status value, belong to inline code, or sit inside an ordinary sentence. A reliable workflow has to establish where a table starts, where it ends, and how many cells each row is expected to contain before it worries about border style.
This guide combines the parser edge cases and repair steps that matter most in real documentation. The behavior described here is checked against the production converter. You can inspect the source, generated output, metrics, and diagnostics for every case in the Parser Compatibility Lab.
Start with the separator row
A Markdown table is established by a header followed immediately by a valid separator row:
| Name | Status |
|---|---|
| API | Ready |
Each separator cell needs at least three dashes, with optional colons for alignment. These are valid:
|:---|:---:|---:|
This is not a valid separator:
| === | pending |
md2text deliberately leaves table-shaped text alone when the separator row is invalid. Requiring that structural signal prevents a shell command or ordinary paragraph containing | from being misidentified as a table.
Tables without leading and trailing pipes are still valid when the separator is clear:
Name | Score
--- | ---:
Alpha | 12
If detection fails, inspect the separator before adjusting whitespace elsewhere.
Keep literal pipes inside their cells
An escaped pipe belongs to the cell content:
| Status |
|---|
| success \| warning |
The converted value is success | warning. The escape marker disappears and the literal pipe does not create a second column.
Inline code needs similar treatment because commands and expressions often contain pipes:
| Example |
|---|
| `cat file | grep error` |
The pipe inside the backticks is code. Both `a | b` and `a \| b` remain one cell in md2text. This matters for shell commands, union types, regular expressions, and fallback expressions.
Markdown link labels can contain pipes too:
| Reference | Notes |
|---|---|
| [A | B migration](https://example.com) | Rollout guide |
The readable text output keeps the label and destination together as A | B migration (https://example.com). The pipe stays inside the label instead of shifting the row.
Protect code and prose boundaries
A fenced code block can contain table-shaped lines or shell pipes:
```sh
cat access.log | grep 500
```
That block is code, not a table. Keep its fences intact when pasting a mixed document. md2text removes the fence markers for plain-text output but preserves the code inside without running table detection on it.
Ordinary prose also stays ordinary unless the following line is a valid separator row:
Use A | B in prose, not as a table.
A pipe alone is not enough evidence. This conservative rule is important for support notes, deployment instructions, and command documentation where prose and shell syntax regularly share the same document.
End a table before prose begins
A particularly risky boundary appears when prose follows a table without a blank line:
| Key | Value |
|---|---|
| A | 1 |
Use A | B for fallback.
The final sentence has one pipe and could superficially look like a two-cell row. Its shape is prose-like, however, so md2text ends the table after A | 1, preserves the sentence in its original order, and emits an informational diagnostic:
Line 4: This looks like prose after a table. Consider adding a blank line after the table.
The Grid ASCII result is:
+-----+-------+
| Key | Value |
+-----+-------+
| A | 1 |
+-----+-------+
Use A | B for fallback.
The conversion is not blocked because a useful result is still possible. Add a blank line after the table in the source when you can; explicit boundaries help future editors as well as parsers.
Repair inconsistent rows deliberately
The header establishes the expected column count. In this example, the body row is one cell short:
| Name | Role | Status |
|---|---|---|
| Sam | Reviewer |
md2text pads the missing cell in its best-effort output and reports the exact source line:
Line 3: Expected 3 columns, found 2. Add the missing cell separators or leave an empty cell such as `| |`.
Use the warning to decide whether the blank was intentional. If it was, make the empty cell explicit:
| Sam | Reviewer | |
If it was not, add the missing value. Diagnostics should point to uncertainty; they should not silently invent document meaning.
Intentional empty cells are supported across Grid ASCII, Unicode box, and Simple text:
| A | B | C |
|---|---|---|
| 1 | | 3 |
| | 2 | |
Their positions remain stable because width and alignment are calculated per column after the row shape is normalized.
Check visible width, not string length
Plain-text alignment depends on terminal display width. JavaScript string length is not enough because CJK characters and many emoji occupy more visual space than ordinary ASCII characters.
| 名称 | 状态 |
|---|---|
| 表格 | 完成 |
md2text uses the same visible-width calculation for padding and the result health summary. That keeps alignment and the reported maximum line width consistent. The CJK and emoji width guide explains the tradeoffs in more detail.
After conversion, use the health status as a destination check:
| Maximum visible width | Status | Practical meaning |
|---|---|---|
| 80 or less | 80-col safe | Suitable for many terminals and source comments |
| 81 to 120 | May wrap | Preview in the destination before publishing |
| More than 120 | Too wide | Shorten cells, split the table, or use Simple text |
No width calculation can control a proportional email font. For destinations that support Markdown, copy the output as a fenced text block so spaces stay monospaced.
Preserve multiple tables in mixed documents
Documentation often contains prose, code, and more than one table. The converter should transform only the valid table blocks and preserve everything else:
Deployment summary
| Service | Status |
|---|---|
| API | Ready |
Notes for support follow here.
Owner | Queue
--- | ---
Platform | Review
md2text reports the total table count, total rows, maximum column count, and maximum visible output line. When there are multiple tables, it also exposes each table’s source line range and output separately so one table can be copied without taking the surrounding paragraphs.
This is safer than treating the entire document as one table-shaped block. It also makes row counts useful: an unexpected increase can reveal that prose was swallowed, while an unexpected decrease can reveal that a separator was invalid.
A repeatable repair workflow
Use this order when a table looks wrong:
- Find the header and verify the next line is a valid dash-and-colon separator.
- Count header cells, then compare each body row with that expected count.
- Escape literal pipes and check pipes inside inline code or link labels.
- Restore fenced code markers around commands and examples.
- Add blank lines where a table meets prose, another table, or a code block.
- Shorten long cells instead of forcing paragraphs into a fixed-width grid.
- Convert in all relevant output styles and inspect diagnostics and maximum line width.
- Preview the final text in the actual email client, terminal, comment box, or support tool.
This sequence starts with structure, then content, then destination fit. Randomly adding spaces rarely fixes the underlying ambiguity.
Verified behavior checklist
The maintained regression suite currently covers:
| Boundary | Expected behavior |
|---|---|
| Escaped pipe | Literal pipe remains inside one cell |
| Inline code pipe | Code expression remains one cell |
| Link label pipe | Link text and URL remain readable |
| Fenced code | No table is detected inside the fence |
| Ordinary prose pipe | Prose is preserved unchanged |
| Prose after table | Table ends and an informational diagnostic appears |
| No outer pipes | Valid separator still establishes the table |
| CJK and emoji | Visible width drives padding and health metrics |
| Missing cells | Best-effort output plus a line-level warning |
| Multiple tables | Each table is converted independently |
| Invalid separator | Source remains ordinary text |
| Empty cells | Column positions remain stable |
See all 15 source fixtures and production-generated results in the Parser Compatibility Lab. The lab and automated tests share one data source, so the public examples cannot quietly drift away from the behavior being tested.
Convert your own table
Paste a Markdown table into the converter and choose ASCII grid, Unicode box, or Simple text output. Conversion runs locally in your browser.
Convert a Markdown table