What you will be able to do
- Diagnose failed or partial loads with COPY output, COPY_HISTORY, VALIDATION_MODE and ON_ERROR
- Fix common load faults such as a broken storage integration link and misleading load timestamps
- Apply UPDATE and DELETE safely to loaded tables, and know how DELETE interacts with load history
- Define external tables, including their columns, partitions and refresh options
1.Finding and resolving data import errors
Each COPY INTO <table> returns one row per file. That row shows STATUS (loaded, load failed or partially loaded), ROWS_PARSED, ROWS_LOADED, ERRORS_SEEN, and the first error together with its line, character position and column name. To look at loads from earlier, the troubleshooting guide begins with COPY_HISTORY. Its STATUS column has the same three outcomes, and its FIRST_ERROR_MESSAGE gives the reason. As the name says, it reports only the first error in each set of files.
ON_ERROR decides what COPY does when a row is bad. Choose the value deliberately. The documentation warns that the default suits common cases but isn't always the best option.
| Value | Behaviour |
|---|---|
| CONTINUE | Keeps loading the file. It reports at most one error per file, and ROWS_PARSED minus ROWS_LOADED is the number of rows that had errors. |
| SKIP_FILE | Skips the whole file on the first error. It buffers every file, so it is slower than CONTINUE or ABORT_STATEMENT. |
| SKIP_FILE_num (e.g. SKIP_FILE_10) | Skips a file once its count of error rows reaches the number given |
| 'SKIP_FILE_num%' | Skips a file based on its percentage of error rows |
| ABORT_STATEMENT | Listed as a value. In Snowsight, choosing Load without other options stops the load on an error. |
To see every error and not just the first, run the same COPY with VALIDATION_MODE. This option loads nothing. The syntax also accepts RETURN_<n>_ROWS and RETURN_ERRORS, but the troubleshooting guide's example uses RETURN_ALL_ERRORS. It then takes the query ID and writes the rejected records to a file on the stage, so you can fix the source data:
COPY INTO mytable
FROM @mystage/myfile.csv.gz
VALIDATION_MODE=RETURN_ALL_ERRORS;
SET qid=last_query_id();
COPY INTO @mystage/errors/load_errors.txt FROM (SELECT rejected_record FROM TABLE(result_scan($qid)));Checkpoint 1 of 7· Put it in order
Put the error-extraction steps in the order they must run
- 1.Capture the query ID with SET qid=last_query_id()
- 2.Unload rejected_record from RESULT_SCAN($qid) to a file on the stage
- 3.Run COPY INTO mytable with VALIDATION_MODE=RETURN_ALL_ERRORS
RESULT_SCAN needs the ID of the validation query, and LAST_QUERY_ID returns that ID only if it runs straight after the validating COPY.
“Note that the statements in this section must be run in succession in order to retrieve the applicable records using the LAST_QUERY_ID function.”Source: docs.snowflake.com
The guide describes two more faults. Error 003139 ('Integration … cannot be found') means a stage has lost its storage integration. Stages point to integrations by a hidden ID, and CREATE OR REPLACE STORAGE INTEGRATION drops the integration and recreates it with a new ID. To fix it, run ALTER STAGE stage_name SET STORAGE_INTEGRATION = storage_integration_name on every affected stage. Load timestamps that look too early usually come from a column defaulting to CURRENT_TIMESTAMP. That value is evaluated when the load is compiled, not when it commits, so it comes out earlier than the LOAD_TIME in COPY_HISTORY. Use METADATA$START_SCAN_TIME instead.
Checkpoint 2 of 7· Check yourself
COPY_HISTORY shows a set of files as partially loaded, and FIRST_ERROR_MESSAGE names one bad date. You need every problem row so you can fix the source files. What should you do next?
FIRST_ERROR_MESSAGE shows only the first error. VALIDATION_MODE=RETURN_ALL_ERRORS reports all of them and loads nothing.
“Execute a COPY statement with the VALIDATION_MODE copy option set to RETURN_ALL_ERRORS.”Source: docs.snowflake.com
Checkpoint 3 of 7· Exam question
A `COPY INTO customers` command for `customers_2026.csv` fails with the error `Number of columns in file (6) does not match that of the corresponding table (5)`. The sixth field is a trailing notes column that the analyst does not need. Which change loads the first five fields of every row?
Correct answer: C — Set `ERROR_ON_COLUMN_COUNT_MISMATCH = FALSE` in the file format so the first five fields load and the sixth is ignored
- A. `ON_ERROR = CONTINUE` would treat every row with six fields as an error and skip it, so the load could succeed while loading no data at all.
- B. `SKIP_HEADER` only skips leading lines of the file. It has no effect on how many fields each data row contains.
- C. Correct. With `ERROR_ON_COLUMN_COUNT_MISMATCH = FALSE`, rows with more fields than the table has columns load their fields in order and the extra fields are not loaded. A `SELECT $1..$5` transformation in the COPY would also work.
- D. `TRUNCATECOLUMNS` shortens string values that exceed the column length. It does not address a mismatch in the number of columns.
2.General DML on loaded tables: INSERT, UPDATE and DELETE
After data is loaded, you change it with ordinary DML. Snowflake's DML group covers INSERT (single-table and multi-table), MERGE, UPDATE, DELETE and TRUNCATE TABLE, and COPY INTO <table> is listed in that group too. PUT, GET, LIST and REMOVE are a separate set. They manage files on stages and don't perform DML. The sources here list INSERT but give no syntax for it, so this section concentrates on UPDATE and DELETE.
UPDATE <target_table>
SET <col_name> = <value> [ , <col_name> = <value> , ... ]
[ FROM <additional_tables> ]
[ WHERE <condition> ]UPDATE without a WHERE clause changes every row. In SET, write the bare column name: UPDATE t1 SET t1.col = 1 is invalid. If FROM joins in another table, one target row can match several source rows. By default Snowflake then updates it from one of them, chosen nondeterministically. Set ERROR_ON_NONDETERMINISTIC_UPDATE = TRUE to make that situation an error.
DELETE FROM <table_name>
[ USING <additional_table_or_query> [, <additional_table_or_query> ] ]
[ WHERE <condition> ]DELETE without WHERE removes every row but keeps the table. USING lets other tables or subqueries pick the rows to remove. The point that ties DML to loading is load history. TRUNCATE TABLE clears the table's external file load history, but DELETE leaves it in place. So if you DELETE rows that came from a staged file, a plain COPY won't load that file again, because its checksum is already recorded. You either modify and re-stage the file, or override the history with FORCE = TRUE, which deliberately reloads unchanged files and can create duplicate rows.
Checkpoint 4 of 7· Fill the gap
Which copy option makes the second statement reload files that were already loaded and haven't changed?
COPY INTO load1 FROM @%load1/data1/ FILES=('test1.csv', 'test2.csv') ? =TRUE;FORCE = TRUE skips the load-history check and reloads files whose checksum is unchanged, which produces duplicate rows. PURGE only removes loaded files from the stage.
Source: docs.snowflake.com3.Preparing external tables
Sometimes the data should stay in cloud storage and not be loaded at all. An external table lets you query files in an external stage as if they were a Snowflake table, while Snowflake stores only file-level metadata. It reads any format COPY supports except XML. External tables are read-only: you can query them, join them and build views on them, but you can't run DML against them. Queries can be slower than on native tables. A materialized view over the external table can help, and for Parquet the documentation suggests Apache Iceberg tables instead. If a query hits a bad file, it skips that file and moves on to the next, so a query can return partial results without failing.
You don't need to know the schema in advance, only the file format. Every external table has three columns. VALUE is a VARIANT holding one row of the file. METADATA$FILENAME is the staged file's name and path. METADATA$FILE_ROW_NUMBER is the row's position within that file. SELECT * always returns VALUE. If you do know the schema, you can add virtual columns defined as expressions over VALUE and the pseudocolumns. Their data types must match the external data, which gives you type checking.
Checkpoint 5 of 7· Match them up
Match each external table column to what it holds
Tap a term, then the definition that fits it.
All three columns exist on every external table. Virtual columns and partition expressions are built from them.
“A VARIANT type column that represents a single row in the external file.”Source: docs.snowflake.com
Snowflake strongly recommends partitioning. It needs files arranged in logical paths, such as by date or country. A partition column is an expression that parses METADATA$FILENAME, and it is declared with PARTITION BY when the table is created. With automatic partitions, Snowflake adds partitions whenever metadata refreshes. That happens once at creation, can be set up to happen when new files arrive, or can be run manually with ALTER EXTERNAL TABLE … REFRESH. With user-specified partitions (PARTITION_TYPE = USER_SPECIFIED), the owner adds partitions using ALTER EXTERNAL TABLE … ADD PARTITION. This option is mainly used to stay in sync with metastores such as AWS Glue or Hive, and it can't be refreshed automatically. Once the table exists, you can't change which method it uses.
CREATE EXTERNAL TABLE
<table_name>
( <part_col_name> <col_type> AS <part_expr> )
[ , ... ]
[ PARTITION BY ( <part_col_name> [, <part_col_name> ... ] ) ]
..| Format | Recommended size range |
|---|---|
| Parquet files | 256 - 512 MB |
| Parquet row groups | 16 - 256 MB |
| All other supported file formats | 16 - 256 MB |
Checkpoint 6 of 7· Check yourself
An external table was created with PARTITION_TYPE = USER_SPECIFIED to stay in sync with AWS Glue. New folders have arrived in the bucket. How do they become partitions?
User-specified partitions are added by hand with ADD PARTITION. Refreshing this kind of table, automatically or manually, isn't supported, and the partitioning method can't be changed after creation.
“Automatically refreshing an external table with user-defined partitions isn’t supported.”Source: docs.snowflake.com
Checkpoint 7 of 7· Exam question
After a faulty transformation, an analyst runs `DELETE FROM orders;`, corrects the file format, and re-runs the same `COPY INTO orders FROM @orders_stage` a few hours later. The earlier load had succeeded, the staged files are unchanged, and the result shows `Copy executed with 0 files processed`. What should the analyst add to reload the files?
Correct answer: A — Add `FORCE = TRUE` to the COPY INTO statement, because load metadata still marks those files as already loaded
- A. Correct. COPY INTO tracks loaded files in load metadata (kept for 64 days) and skips them even if the table rows were deleted. `FORCE = TRUE` loads them again, at the cost of possible duplicates if rows still exist.
- B. `ALTER STAGE ... REFRESH` updates directory table metadata only. It does not touch the COPY load history used to skip files.
- C. `PURGE = TRUE` deletes successfully loaded files from the stage after the load. It does not clear load history, and it would remove the files you want to reload.
- D. `ON_ERROR = SKIP_FILE` controls what happens when a file has errors. The files here were loaded without errors, so this setting changes nothing.
Sources6
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.COPY_HISTORY's FIRST_ERROR_MESSAGE tells you everything that is wrong with a failed set of files.Why is that wrong?
It shows only the first error. To see every error, use VALIDATION_MODE = RETURN_ALL_ERRORS, which loads nothing.
Covered in Finding and resolving data import errors
2.After a DELETE of bad rows, re-running the same COPY reloads the corrected data from the same staged file.Why is that wrong?
DELETE leaves the file load history in place, so COPY treats the unchanged file as already loaded. Modify and re-stage the file, or use FORCE = TRUE.
Covered in General DML on loaded tables: INSERT, UPDATE and DELETE
3.You can fix bad values in an external table with UPDATE or DELETE.Why is that wrong?
External tables are read-only. Fix the files in cloud storage, or load the data into a native table and change it there.
Covered in Preparing external tables
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“The STATUS column indicates whether a particular set of files was loaded, partially loaded, or failed to load.”
↩︎ Finding and resolving data import errors“No data is loaded when this copy option is specified.”
↩︎ Finding and resolving data import errors“A stage links to a storage integration using a hidden ID rather than the name of the storage integration.”
↩︎ Finding and resolving data import errors“It is recommended to include and query METADATA$START_SCAN_TIME instead, which provides a more accurate representation of record loading.”
↩︎ Finding and resolving data import errors“the FIRST_ERROR_MESSAGE column only indicates the first error encountered.”
↩︎ Exam trap 1“Note that the statements in this section must be run in succession in order to retrieve the applicable records using the LAST_QUERY_ID function.”
↩︎ Checkpoint“Execute a COPY statement with the VALIDATION_MODE copy option set to RETURN_ALL_ERRORS.”
↩︎ Checkpoint - 2.
“For this reason, SKIP_FILE is slower than either CONTINUE or ABORT_STATEMENT.”
↩︎ Finding and resolving data import errors“Add FORCE = TRUE to a COPY command to reload (duplicate) data from a set of staged data files that have not changed”
↩︎ General DML on loaded tables: INSERT, UPDATE and DELETE - 3.https://docs.snowflake.com/en/sql-reference/sql-dmlOfficial docs
“Commands for inserting, deleting, updating, and merging data in Snowflake tables:”
↩︎ General DML on loaded tables: INSERT, UPDATE and DELETE - 4.
“Do not include the table name. For example, UPDATE t1 SET t1.col = 1 is invalid.”
↩︎ General DML on loaded tables: INSERT, UPDATE and DELETE - 5.
“If this parameter is omitted, all rows in the table are removed, but the table remains.”
↩︎ General DML on loaded tables: INSERT, UPDATE and DELETE“Unlike TRUNCATE TABLE, this command does not delete the external file load history.”
↩︎ Exam trap 2 - 6.
“External tables can access data stored in any format that the COPY INTO <table> command supports, except XML.”
↩︎ Preparing external tables“After an external table is created, the method by which partitions are added can’t be changed.”
↩︎ Preparing external tables“External tables are read-only. You can’t perform data manipulation language (DML) operations on external tables.”
↩︎ Exam trap 3“A VARIANT type column that represents a single row in the external file.”
↩︎ Checkpoint“Automatically refreshing an external table with user-defined partitions isn’t supported.”
↩︎ Checkpoint