A client refuses a Delta Lake table, citing its protocol version — what does that mean?
answer
- not every engine understands every capability
- one action states the minimum client capability
- two numbers: one to read, one to write
- named capabilities replaced the numeric ladder
- writer-only capabilities still let old readers in
basics
~20 sEvery 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 sThe `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{"protocol":{"minReaderVersion":1,"minWriterVersion":2}}
{"protocol":{"minReaderVersion":3,"minWriterVersion":7,"readerFeatures":["deletionVectors"],"writerFeatures":["deletionVectors","appendOnly","invariants"]}}go deeper
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.
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.
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.
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