skip to content

A client refuses a Delta Lake table, citing its protocol version — what does that mean?

level: seniorimportance: should knowfreq 44%

answer

  1. not every engine understands every capability
  2. one action states the minimum client capability
  3. two numbers: one to read, one to write
  4. named capabilities replaced the numeric ladder
  5. writer-only capabilities still let old readers in

basics

~20 s

Every Delta table records a protocol action with minReaderVersion and minWriterVersion; a client that supports less must refuse the table rather than misread it. Reader 3 and writer 7 switch to named table features listed in readerFeatures and writerFeatures.

solid answer

~50 s

The `protocol` action in `_delta_log` states the minimum capability a client needs: `minReaderVersion` to read the table at all, `minWriterVersion` to write to it. This is a **safety** mechanism — a table using column mapping or deletion vectors would be read *incorrectly* by an unaware client, so refusing is the correct behaviour, not a bug. Numbered versions were a coarse ladder (each bump bundled several features), so Delta introduced **table features**: reader version 3 and writer version 7 mean "consult the `readerFeatures` and `writerFeatures` lists instead", each naming a capability such as `deletionVectors`, `columnMapping`, `changeDataFeed` or `timestampNtz`. A writer-only feature still lets old readers read. Enabling a feature — usually via `ALTER TABLE ... SET TBLPROPERTIES` — upgrades the protocol permanently for every consumer, so check what else reads the table before you do it.

code

json · 3 lines
json
{"protocol":{"minReaderVersion":1,"minWriterVersion":2}}

{"protocol":{"minReaderVersion":3,"minWriterVersion":7,"readerFeatures":["deletionVectors"],"writerFeatures":["deletionVectors","appendOnly","invariants"]}}

go deeper

for a junior

Recall that a Delta table declares minimum reader and writer versions in its log, and that a client below them refuses the table on purpose rather than risking wrong results.

for a middle

Explain why numbered versions gave way to named table features at reader 3 / writer 7, and what distinguishes a writer-only feature from a reader-writer one.

for a senior

Handle the incident: identify which feature was enabled and when, tell an old client apart from a genuine corruption, and know that the upgrade is committed to the log and not undone by resetting a property.

for a principal

Treat protocol level as a published contract. Inventory every consumer of a shared table, schedule upgrades like breaking API changes, and set platform defaults so a new runtime cannot silently orphan an older reader.

## What the protocol action is for Delta's log is an open format that many engines read: Spark, Trino, Flink, DuckDB, Databricks runtimes of many vintages, and vendor connectors. New capabilities keep being added, and some of them change *how the existing actions must be interpreted*. Column mapping means the Parquet column names no longer match the logical schema. Deletion vectors mean a live `add` action no longer implies every row in that file is visible. A client that ignored either would not fail — it would return **wrong data**, silently. So every Delta table carries a `protocol` action naming the minimum client capability required: ```json {"protocol":{"minReaderVersion":1,"minWriterVersion":2}} ``` A client whose supported reader version is below `minReaderVersion` must refuse to read; below `minWriterVersion`, it must refuse to write but may still read. Refusal is the designed behaviour. When someone reports "Delta broke my pipeline with a protocol error", the correct reading is that the pipeline was saved from returning wrong results. ## The numbered ladder and why it ran out Originally each protocol bump bundled features. On the writer side, later versions successively required support for append-only tables and column invariants, CHECK constraints, change data feed and generated columns, column mapping, and identity columns; on the reader side, column mapping was the notable bump. The design flaw is that the ladder is a total order: to use a feature introduced at writer version 5 you had to declare support for everything at 2, 3 and 4 as well, even if your table used none of it. That over-restricts which engines can participate. ## Table features Delta replaced the ladder with **table features**. Reader version 3 and writer version 7 are sentinel values meaning "the numeric version no longer describes this table; read the feature lists". The protocol action then carries explicit names: ```json {"protocol":{"minReaderVersion":3,"minWriterVersion":7, "readerFeatures":["deletionVectors"], "writerFeatures":["deletionVectors","appendOnly","invariants"]}} ``` A client now checks whether it implements each named feature. Features come in two shapes: - **Writer-only features** — the on-disk data is interpretable by any reader; only writers need special handling. `changeDataFeed`, `generatedColumns`, `appendOnly`, `invariants` and `checkConstraints` are of this kind. Old readers keep working. - **Reader-writer features** — the feature changes how existing data must be read. `deletionVectors`, `columnMapping`, `timestampNtz` and `v2Checkpoint` are of this kind, and they appear in both lists. Any reader lacking them must refuse. The distinction is the single most useful thing to know here: enabling change data feed does not lock out old readers; enabling deletion vectors or column mapping does. ## Enabling and un-enabling Features are turned on either by a table property that implies them or by explicitly listing them, both through `ALTER TABLE ... SET TBLPROPERTIES`. Setting `delta.enableDeletionVectors = true`, for example, raises the protocol to reader 3 / writer 7 and adds `deletionVectors` to both feature lists. The upgrade is written into the log as a new `protocol` action and applies to the table for everyone, immediately and permanently — there is no per-client opt-out and no downgrade by lowering the property back. Recent Delta versions do support `ALTER TABLE ... DROP FEATURE` for some features, which removes the feature and any traces of it from history, but that is a deliberate maintenance operation, not an undo button. A concurrent protocol change is also a conflict source: a transaction planned against the old protocol that finds a new `protocol` action committed underneath it fails with a protocol-changed conflict and must re-plan. ## How this bites in production The recurring incident is asymmetric consumers. A Databricks job on a recent runtime enables a feature — often implicitly, by enabling a table property someone read about — and the next morning an older Spark cluster, a Trino cluster on an older connector, or a vendor tool that reads the same table starts failing. Nothing is corrupt; the table simply now requires a capability those clients do not have. Recovery means either upgrading every consumer or, where supported, dropping the feature. The defensive habits are: know your table's protocol (`DESCRIBE DETAIL` reports the reader and writer versions), inventory who reads each shared table before enabling anything, and treat a protocol upgrade as a breaking change to a published contract — announced and scheduled, not applied casually to a shared table. Managed platforms make this easier to trip over precisely because the default table properties on a new runtime may already imply a higher protocol than your oldest consumer supports.

  • Why is refusing to read a better outcome than reading the table anyway?
    Because some features change interpretation rather than adding to it. With column mapping the Parquet column names no longer match the schema; with deletion vectors a live file no longer means all its rows are visible. An unaware reader would return wrong data with no error, which is far worse than a clear refusal.
  • What is the difference between a writer-only feature and a reader-writer feature?
    A writer-only feature — change data feed, generated columns, append-only, invariants — only constrains how writers behave; the data on disk stays interpretable, so existing readers keep working. A reader-writer feature such as deletion vectors, column mapping or timestampNtz changes how the data must be read, so it appears in both lists and locks out unaware readers.
  • Can you undo a protocol upgrade by resetting the table property?
    Not by resetting the property. The upgrade is a committed `protocol` action and applies to everyone. Recent Delta versions offer `ALTER TABLE ... DROP FEATURE`, which removes the feature and, where needed, truncates the history that used it — a deliberate maintenance operation, not a rollback.

saying these in an interview costs you the question

  • Treating a protocol mismatch as a bug rather than a safety refusal
  • Assuming raising the protocol is reversible by resetting a property
  • Thinking all table features lock out old readers equally
  • Believing the protocol version describes the file format rather than client capability
  • Enabling a feature on a shared table without checking other consumers

context