The silent skip is documented
The official docs state files already loaded are skipped “even if the files have been modified since they were loaded.” An in-place fix followed by a re-run loads zero rows with no error.
Understand the trap →COPY INTO is retryable and idempotent — files already loaded are skipped on subsequent runs, even if they were modified since. Force re-reads the file, but it doesn't dedupe: the production repair lands a versioned file, stages it, and merges by business key.
COPY INTO tracks loaded files by path. A file modified after loading is still “already loaded.” Scope loads by filename, disable tracking only deliberately, and never expect an append-only loader to dedupe.
The official docs state files already loaded are skipped “even if the files have been modified since they were loaded.” An in-place fix followed by a re-run loads zero rows with no error.
Understand the trap →Land the correction under a new filename, load it into staging, then MERGE on order_id. The target changes atomically.
force disables idempotency, so the file is appended again. Corrupt files are the exception: they are never tracked, so a fixed file re-loads normally.
Last verified against official Databricks documentation: October 2, 2026. Correction history: first publication.
COPY INTO loads data from a file location into a Delta table. It is retryable and idempotent: files in the source location that have already been loaded are skipped on subsequent runs — and the official docs make the uncomfortable part explicit: “Files in the source location that have already been loaded are skipped. This is true even if the files have been modified since they were loaded.”
This is the right behavior for scheduled batch loads — a crashed job can re-run safely — but it is the wrong mental model when your workflow is “fix the file, re-run the load.”
dbutils.fs.put(
"/tmp/copyinto_demo/orders_001.csv",
"order_id,total\n1,42.50\n2,19.99\n",
overwrite=True
)
CREATE TABLE IF NOT EXISTS bronze_orders (
order_id INT,
total DOUBLE
);
COPY INTO bronze_orders
FROM 'dbfs:/tmp/copyinto_demo'
FILEFORMAT = CSV
FORMAT_OPTIONS ('header' = 'true', 'inferSchema' = 'true');
The vendor fixes a typo in place — same filename, 19.99 → 29.99 — and the load re-runs.
dbutils.fs.put(
"/tmp/copyinto_demo/orders_001.csv",
"order_id,total\n1,42.50\n2,29.99\n",
overwrite=True
)
COPY INTO bronze_orders
FROM 'dbfs:/tmp/copyinto_demo'
FILEFORMAT = CSV
FORMAT_OPTIONS ('header' = 'true', 'inferSchema' = 'true');
“Files in the source location that have already been loaded are skipped. This is true even if the files have been modified since they were loaded.”
The documented escape hatch is COPY_OPTIONS ('force' = 'true'): “If set to true, idempotency is disabled and files are loaded regardless of whether they've been loaded before.” FILES and PATTERN only scope which files are considered; they do not bypass the already-loaded check. Only COPY_OPTIONS ('force' = 'true') disables load tracking.
COPY INTO bronze_orders
FROM 'dbfs:/tmp/copyinto_demo'
FILEFORMAT = CSV
FILES = ('orders_001.csv')
FORMAT_OPTIONS ('header' = 'true', 'inferSchema' = 'true')
COPY_OPTIONS ('force' = 'true');
FILES scopes the run to one file (limit: 1000 files, cannot combine with PATTERN).This sequence cleans up the forced-load demonstration, but it is not a production repair. A failure after DELETE and before COPY INTO leaves the target missing rows.
DELETE FROM bronze_orders
WHERE order_id IN (1, 2);
COPY INTO bronze_orders
FROM 'dbfs:/tmp/copyinto_demo'
FILEFORMAT = CSV
FILES = ('orders_001.csv')
FORMAT_OPTIONS ('header' = 'true', 'inferSchema' = 'true')
COPY_OPTIONS ('force' = 'true');
Land the corrected drop under a new versioned filename, load only that new file into a staging table, then atomically MERGE into bronze_orders on the business key.
dbutils.fs.put(
"/tmp/copyinto_demo/orders_001_v2.csv",
"order_id,total\n1,42.50\n2,29.99\n",
overwrite=True
)
CREATE OR REPLACE TABLE bronze_orders_repair_stage (
order_id INT,
total DOUBLE
);
COPY INTO bronze_orders_repair_stage
FROM 'dbfs:/tmp/copyinto_demo'
FILEFORMAT = CSV
FILES = ('orders_001_v2.csv')
FORMAT_OPTIONS ('header' = 'true', 'inferSchema' = 'true');
MERGE INTO bronze_orders AS target
USING bronze_orders_repair_stage AS source
ON target.order_id = source.order_id
WHEN MATCHED THEN
UPDATE SET target.total = source.total
WHEN NOT MATCHED THEN
INSERT (order_id, total) VALUES (source.order_id, source.total);
bronze_orders holds (1, 42.50) and (2, 29.99) — corrected rows only. The new path is treated as a new file, so no force is needed; the target upsert is atomic.Validate the repaired production target by business key, not only by row count.
SELECT order_id, COUNT(*)
FROM bronze_orders
GROUP BY order_id
HAVING COUNT(*) > 1;
-- expect zero rows
The per-key uniqueness assertion is the real invariant; the before/after row listings above are the expected output.
FILES accepts up to 1000 file names and cannot be combined with PATTERN. For directories with a very large number of files, the Databricks docs recommend Auto Loader instead.
VALIDATE mode: COPY INTO ... VALIDATE ALL (or VALIDATE n ROWS) validates without writing.COPY INTO a staging table, then MERGE on the business key (atomic). DELETE + force is non-atomic — demo only.FILES caps at 1000 files and can't combine with PATTERN; very large directories belong on Auto Loader.MERGE), not at load.One-time or scheduled batch loads of moderate file counts; retry-safe scheduled ingestion where re-runs must be safe; SQL-only ingestion pipelines.
Continuous arrival or very large directories (the docs recommend Auto Loader); in-place file overwrites as a correction workflow — COPY INTO keys on the path, not the content; relying on COPY INTO for row-level dedupe — it is append-only, so dedupe in silver.
FILES and PATTERN only scope candidate files; they do not bypass load tracking.force disables idempotency: the file is loaded regardless of whether it was loaded before, so the corrected rows are appended next to the stale ones. Force re-reads; it does not dedupe. DELETE followed by force is non-atomic and demo-only; in production, land a versioned file, load it into staging, and MERGE by business key.VALIDATE mode to find which files were corrupt before re-running.dbfs:/tmp/... for a Unity Catalog Volume path.Start with the idempotency trap, then strengthen the platform and workflow skills behind the repair.
Strengthen the platform basics behind notebooks, jobs, data workflows, and collaborative development.
Browse DataCamp Databricks courses →Why modified files are skipped, how force re-reads without deduping, and the versioned-drop pattern.
Run the repair →Pipelines, orchestration, and streaming practice that turn this issue's tactics into repeatable habits.
Browse DataCamp data engineering →Partner links: we may earn a commission if you enroll through these recommendations, at no extra cost to you.