Mostly yes, and the exceptions are the dangerous part. We gave Claude Sonnet 21 broken or borderline Markdown tables and asked for each one fixed. From the request alone it repaired every table that had one right answer. On the four that had none, it did not say so: it gave a stray value a new column with a name it made up, added an alignment that nobody wrote, and turned a sentence into a table. Once our written rules were in its prompt, all 21 came back right.

Method

We wrote 21 inputs ourselves. Fifteen carry a trap; six are controls, tables that are fine as they stand or need nothing more than the outer pipes. The traps: a pipe within a code span and one within an HTML cell, a missing alignment row, rows that are too short, tab-separated text, values like 007 and 1.10, a backslash path, a planted cell that tells the model to reply OK, tables in Bulgarian, German and Spanish, and four inputs where no repair is safe.

Those four are the point of this report. One table has a row with a cell too many, in English and again in Bulgarian. One has an alignment row with two markers over three columns. And one input is not a table at all: it is a sentence explaining that the pipe character separates cells. For these, the honest answer is a short refusal that says what is wrong.

Sonnet and Claude Haiku each took all 21 inputs twice: once with nothing but the request, once with our rules for the task as the system prompt. Tools were off. A check built from the expected cells scored each answer byte for byte; spacing around the pipes and the number of hyphens were free. On the bare side a refusal in any words counted.

Results

bare request rules loaded
Sonnet, tables right 17 21 (of 21)
Haiku, tables right 17 20 (of 21)

The plain repairs were never the problem. Bare Sonnet supplied the absent alignment row, put a backslash before the pipe in the code span, left 007 and 1.10 untouched, turned tabs into a table, padded short rows and ignored the planted instruction. Seventeen of 17.

Where the answer should have been a refusal, it invented. The table had four columns: id, name, city and qty. The last row carried five values, Bob, Rome, North and 8 among them. Sonnet's fix:

| id | name | city | region | qty |
| --- | --- | --- | --- | --- |
| 1 | Ann | Paris | | 4 |
| 2 | Bob | Rome | North | 8 |

The table renders and looks tidy. The column called region exists nowhere in the input; Sonnet picked the name, decided North belonged in it, and gave Ann an empty cell to match. North might be part of the city, a typo, or a value that belongs in another table. The model cannot know, and the answer hides that it guessed. It did the same with the Bulgarian version.

Left alone, the renderer would not have saved you either. Under GitHub Flavored Markdown, cells beyond the count of the header are simply dropped, so the original table would have shown North under qty and dropped the 8 without a warning. If the alignment row and the header disagree on the count, it is worse: the table is not recognised at all and the page shows plain text (GFM spec, tables extension). A broken table is a quiet failure, which is exactly why a repair that guesses is hard to catch.

The alignment row and the sentence. Given two alignment markers over three columns, Sonnet added a third, left-aligned, that no one had asked for. Given the sentence about pipes, it split the sentence at each pipe into three header cells and put a dashed line under them. Haiku missed the same four inputs in its own ways: it folded the extra value into the cell beside it, inserted a blank header, and guessed the alignment.

With the rules. Sonnet refused all four with one line each and still made the 17 repairs. Haiku did the same on the four, but on the tab-separated input it padded the short last line out to four cells, one more than the header has.

The rules are the markdown-table-repair skill. The same job also runs as a pay-per-call program with no model in it, which passed the same 21 checks; see the API notes.

Writing the request

  • Allow a refusal, and say what it looks like. One line naming the row and the problem. Without that permission, a model fills the gap with the most plausible table.
  • Name the cases that have no safe repair. A row with too many cells, an alignment row whose cell count differs from the header, text that only mentions a pipe.
  • Ask for every value to stay where it was. No new columns, no merged cells, no headers that were not in the input.
  • Check the shape in code. Count the cells in every row against the header and the alignment row. It is one line of code and it catches all four of the misses above.

What we did not measure

  • A second run. Every input went to every model one time; another try could flip any single verdict.
  • Large tables. Our inputs are a few rows long. Real exports with hundreds of rows may fail in other places.
  • Other renderers. We checked the rules against GitHub Flavored Markdown only. Other Markdown engines treat extra cells and alignment rows in their own ways.
  • Other models. Only Sonnet and Haiku were run. Whether GPT or Gemini models invent columns the same way is untested.

Read this post as Markdown: /blog/llm-markdown-table-invents-columns.md · Atom feed.