spectreconsole/spectre.console · error · InvalidOperationException

Column spanning is not supported in table header rows.

Error message

Column spanning is not supported in table header rows.

What it means

The TableColumn.Header setter stores the per-column header renderable. Spectre.Console supports column spanning only in body rows, not in the header row; assigning a TableCell whose ColumnSpan > 1 to Header throws InvalidOperationException. This is enforced because header layout has no merging logic.

Source

Thrown at src/Spectre.Console/Widgets/Table/TableColumn.cs:21

/// <summary>
/// Represents a table column.
/// </summary>
public sealed class TableColumn : IColumn
{
    private IRenderable _header = null!;
    private IRenderable? _footer;

    /// <summary>
    /// Gets or sets the column header.
    /// </summary>
    public IRenderable Header
    {
        get => _header;
        set
        {
            if (value is TableCell cell && cell.ColumnSpan > 1)
            {
                throw new InvalidOperationException("Column spanning is not supported in table header rows.");
            }

            _header = value ?? throw new ArgumentNullException(nameof(value));
        }
    }

    /// <summary>
    /// Gets or sets the column footer.
    /// </summary>
    public IRenderable? Footer
    {
        get => _footer;
        set
        {
            if (value is TableCell cell && cell.ColumnSpan > 1)
            {
                throw new InvalidOperationException("Column spanning is not supported in table footer rows.");
            }

View on GitHub (pinned to 0acc92fada)

Solutions

  1. Use a plain renderable (new Markup(text)) for headers instead of a spanning TableCell.
  2. If you hold a TableCell, reset or avoid its span before assigning to Header, or create a fresh non-spanning cell.
  3. Build header content via the string overload new TableColumn("Header text") which wraps a Markup, never a spanned TableCell.

Example fix

// before
column.Header = new TableCell("Merged").Span(2);

// after
column.Header = new Markup("Header text");
Defensive patterns

Strategy: validation

Validate before calling

var headerCell = value as TableCell;
if (headerCell is not null && headerCell.ColumnSpan > 1)
    throw new InvalidOperationException("Headers cannot span columns.");
column.Header = value;

Type guard

static bool IsSafeHeader(IRenderable r) => r is not TableCell tc || tc.ColumnSpan <= 1;

Prevention

When it happens

Trigger: Setting column.Header = new TableCell(markup).Span(2); constructing new TableColumn(someSpanningTableCell); or reusing a body TableCell (which was given a span) as a header renderable.

Common situations: Reusing a TableCell object across header and body by mistake; attempting to visually merge header columns (unsupported feature); building headers from the same factory that produces spanning body cells; assuming span is a global cell property rather than body-only.

Related errors


AI-assisted analysis of spectreconsole/spectre.console@0acc92fada (2026-08-13). Data as JSON: /api/errors/1a1cbc5ec767ad95. Report an issue: GitHub.