Lakehouse Digest
ISSUE 026 · OCTOBER 14, 2026

Your table evolved without asking — the silent schema auto-merge

Schema enforcement is loud by default. One shared session setting can turn that safety rail into silent, table-wide schema drift.

THE THREE-PART READ

Make widening an explicit decision.

Opt in per write on bronze and landing. For silver and gold, add columns only through a reviewed schema change.

TIP · 3 MIN

The config is the blast radius

A session-wide switch can silently widen every Delta table touched by later writes.

Scope the opt-in →
IMPLEMENTATION · 4 MIN

Watch a column appear

An idempotent SQL demo shows the schema before, the trap, the new column, and the safe reset.

Run the demo →
DEV Q&A · 2 MIN

Why MERGE still ignored it

Evolution needs both an opt-in and a real source-schema column covered by the MERGE action.

Read the two gates →

Last verified against official Databricks docs: October 14, 2026. Corrections: none yet — first published as Issue 026, October 14, 2026.

The default fails loudly — until one line changes the session

For a Delta INSERT, an unknown target column fails with UNRESOLVED_COLUMN.WITH_SUGGESTION (SQLSTATE 42703). For MERGE with INSERT * or UPDATE SET *, enforcement requires every target column in the source but silently ignores source columns that do not exist in the target.

SET spark.databricks.delta.schema.autoMerge.enabled = true;
Dangerous scope: Databricks labels this legacy session-wide configuration “Not recommended for production use.” Set it once on a shared session or cluster, and every compatible write in that SparkSession can silently widen every Delta table it touches.

Three sanctioned per-operation opt-ins

  1. INSERT, batch, or streaming writes: set .option("mergeSchema", "true") on the individual bronze or landing write.
  2. INSERT and MERGE: use SQL WITH SCHEMA EVOLUTION or the table API .withSchemaEvolution(). These forms require Databricks Runtime 15.4 LTS or above.
  3. COPY INTO: use COPY_OPTIONS ('mergeSchema' = 'true') for that load.

What additive evolution actually does

New columns append at the end of the target schema, their case is preserved, and old rows read NULL for those columns. For MERGE, the new column must exist in the source schema and either be named in an assignment or be covered by UPDATE SET * / INSERT *. A computed expression can populate a source column, but assigning an expression to a new target name does not itself define that missing source-schema column.

Documented blast radius: “Schema updates conflict with all concurrent write operations.” Also: “Updating a table schema terminates any streams reading from that table.” Coordinate the change, then restart affected readers.

Automatic evolution is for additive columns. Renames, type changes, and drops require explicit operations such as overwriteSchema or Delta column mapping, depending on the change. For silver and gold, keep evolution reviewed and explicit: ALTER TABLE … ADD COLUMN.

Setup — rerunnable and self-contained

SET spark.databricks.delta.schema.autoMerge.enabled = false;

CREATE OR REPLACE TABLE demo_evolve (
  id INT,
  amount BIGINT
);

INSERT INTO demo_evolve VALUES (1, 100);

CREATE OR REPLACE TEMP VIEW demo_source AS
SELECT 2 AS id, 200 AS amount, 'vip' AS tier;

Before

DESCRIBE demo_evolve;
Expected output — deterministic from setup: 2 schema rows: (id, int) and (amount, bigint).

The trap

SET spark.databricks.delta.schema.autoMerge.enabled = true;

INSERT INTO demo_evolve
SELECT * FROM demo_source;

After

DESCRIBE demo_evolve;

SELECT *
FROM demo_evolve
ORDER BY id;
Expected output — deterministic from setup: DESCRIBE returns 3 schema rows: (id, int), (amount, bigint), (tier, string). The ordered data result returns 2 rows: (1, 100, NULL) and (2, 200, 'vip').
ILLUSTRATIVE SCENARIO — not a measured incident: a shared cluster keeps the setting enabled. An exploratory SELECT * write carries an extra column into production. The column appends at the end, positional extracts shift, streaming readers terminate, and concurrent writers can conflict. This describes documented mechanics applied to a hypothetical sequence; it is not an observed event or measured result.

Restore the enforcement default, then prove the failure

The first two statements intentionally reset the demo table to its original two-column state. Without that reset, tier would already exist and the final INSERT would succeed.

SET spark.databricks.delta.schema.autoMerge.enabled = false;

CREATE OR REPLACE TABLE demo_evolve (
  id INT,
  amount BIGINT
);

INSERT INTO demo_evolve VALUES (1, 100);

INSERT INTO demo_evolve (id, tier)
VALUES (3, 'x');
Expected result — deterministic from the reset schema: the final statement fails with UNRESOLVED_COLUMN.WITH_SUGGESTION (SQLSTATE 42703) because tier is not a target column.

The deliberate silver / gold path

ALTER TABLE demo_evolve ADD COLUMN tier STRING;

INSERT INTO demo_evolve (id, tier)
VALUES (3, 'x');

The reviewed DDL changes the contract before the data arrives. The insert is then valid, and the schema change remains visible in table history.

Use this when

  1. Bronze or landing only: additive source columns are expected and the individual write owns the schema contract.
  2. Named-column consumers: downstream readers do not depend on ordinal positions.
  3. Coordinated maintenance: you can restart streaming readers and avoid concurrent schema-changing writes.

Avoid this when

  1. Silver or gold: evolve through reviewed ALTER TABLE only.
  2. Positional readers: new columns append at the end and can change positional extracts.
  3. Streaming readers or CDF consumers: coordinate any explicit schema change; schema updates terminate streams reading the table.
  4. Session- or cluster-wide: never enable auto-merge as ambient configuration.
I set spark.databricks.delta.schema.autoMerge.enabled to true, ran my MERGE with INSERT *, and the new source column STILL didn't land in the target. Why?

Gate 1 — opt in: use the session configuration, SQL WITH SCHEMA EVOLUTION, or .withSchemaEvolution(). Without an evolution opt-in, enforcement keeps the target schema unchanged and INSERT * / UPDATE SET * ignores extra source columns.

Gate 2 — prove the source column: the column must exist in the source schema and be named in an assignment or covered by INSERT * / UPDATE SET *. Assigning a computed expression to a new target name does not define a missing source-schema column.

Practitioner pattern — not official documentation: diff target-table columns against source columns before the merge, then apply the delta through an explicit reviewed ALTER TABLE. This pattern is discussed in the Databricks Community thread “MergeSchema Not Working,” verified loading October 14, 2026.

Version floor: SQL WITH SCHEMA EVOLUTION and the table API .withSchemaEvolution() require Databricks Runtime 15.4 LTS or above.

Snapshot before the write

CREATE OR REPLACE TEMP VIEW schema_before AS
SELECT column_name,
       full_data_type,
       ordinal_position
FROM my_catalog.information_schema.columns
WHERE table_schema = current_schema()
  AND table_name = 'demo_evolve';
Use your catalog: replace my_catalog with the table's catalog. CREATE VIEW … AS DESCRIBE is not valid Spark SQL, so snapshot information_schema.columns instead.

Diff after the write

SELECT column_name,
       full_data_type,
       ordinal_position
FROM my_catalog.information_schema.columns
WHERE table_schema = current_schema()
  AND table_name = 'demo_evolve'
EXCEPT
SELECT column_name,
       full_data_type,
       ordinal_position
FROM schema_before;
Gate: any returned row is an unapproved new or changed schema entry. Stop promotion until the change is reviewed.

Five-line gate checklist

  1. Session auto-merge configuration is never set anywhere.
  2. Evolution is enabled per write on bronze or landing only.
  3. Silver and gold evolve only through reviewed ALTER TABLE.
  4. DESCRIBE HISTORY is the audit trail for the table change.
  5. Mechanics check: new columns append at the end, old rows read NULL, and a schema change terminates streams reading the table.
RECOMMENDED LEARNING

Own the schema contract.

Build the Databricks and data-engineering skills behind deliberate Delta Lake schema management.

Partner links: we may earn a commission if you enroll through these recommendations, at no extra cost to you.

Primary sources

Official documentation

  1. Update table schemas with schema evolution
  2. Schema enforcement
  3. COPY INTO reference

Practitioner (not official documentation)

  1. MergeSchema Not Working — Databricks Community. The page was verified loading October 14, 2026.
Assumptions: Databricks Runtime; all-purpose compute, jobs compute, or SQL warehouse; Delta Lake tables. SQL WITH SCHEMA EVOLUTION and .withSchemaEvolution() require Databricks Runtime 15.4 LTS or above.
Applies to: additive Delta schema evolution through INSERT, batch or streaming writes, MERGE, and COPY INTO, with the operation-specific opt-ins described above.
Last verified against official Databricks docs: October 14, 2026.
Corrections: none yet — first published as Issue 026, October 14, 2026.