What you will be able to do
- Pick the right ON_ERROR behaviour for classic Snowpipe Streaming and know how the high-performance architecture handles bad rows
- Read channel status and HTTP codes to decide whether to retry, reopen the channel, rebuild the client, or fix credentials
- Turn on error logging, query ERROR_TABLE, and repair and reinsert rejected rows
1.How bad rows are handled in each architecture
Snowpipe Streaming handles bad rows differently depending on the architecture, and that difference decides where you look for errors.
In the classic architecture, your own Java application sends rows through the ingest SDK, and it has to handle the errors that come back. For each batch of rows, the API supports the equivalent of three ON_ERROR settings:
| Option | Behaviour |
|---|---|
| CONTINUE | Continue to load the acceptable rows of data and return all errors |
| SKIP_BATCH | Skip loading and return all errors if any error is encountered in the entire batch of rows |
| ABORT (default) | Abort the entire batch of rows and throw an exception when the first error is encountered |
The high-performance architecture processes rows on the server, in Snowflake. It behaves as if ON_ERROR = CONTINUE were always set: valid rows are ingested and bad rows are skipped. The batch doesn't fail, so you need other tools to find out what was dropped. Those tools are channel status and error tables, covered in the next two sections.
Checkpoint 1 of 8· Check yourself
A classic Snowpipe Streaming application doesn't set an error option. A batch arrives with one malformed row. What happens?
ABORT is the default in the classic architecture. Error tables exist only in the high-performance architecture.
“ABORT (default setting): Abort the entire batch of rows and throw an exception when the first error is encountered.”Source: docs.snowflake.com
2.Channel status and choosing the right recovery
In the high-performance architecture, the channel status endpoint shows a channel's current state. Alongside the classic fields (statusCode, persistedOffsetToken), it returns channel_status_code (the channel's health), last_commited_offset_token, rows_parsed, rows_inserted and rows_error_count. For the most recent error it returns last_error_message, last_error_timestamp and last_error_offset_upper_bound, which gives the approximate position of the error in the stream. For history rather than the current state, query the SNOWPIPE_STREAMING_CHANNEL_HISTORY view or the event table telemetry.
For transient failures (HTTP 5XX, 429 and 408), the SDK retries sending unflushed data by itself. Any channel_status_code other than SUCCESS means the channel is invalid and you must close and reopen it. If the code is ERR_PIPE_DOES_NOT_EXIST_OR_NOT_AUTHORIZED or ERR_TABLE_DOES_NOT_EXIST_NOT_AUTHORIZED, fix the pipe or table first, then reopen. Sequencer errors and row-sequence gaps (ERR_CHANNEL_MUST_BE_REOPENED_DUE_TO_ROW_SEQ_GAP, for example) only need a reopen.
There is one exception to the always-CONTINUE behaviour. A schema evolution failure caused by user error invalidates the channel even when ON_ERROR=CONTINUE. Two examples are column names that can't be mapped and adding more columns in one batch than the configured limit allows.
Authorization errors (401 and 403) work the other way. Reopening won't help, because the new channel hits the same problem. Stop ingestion and fix the pipe permissions, user role or credentials, then resume. In the Java SDK, catch SFException and check getHttpStatusCode():
| Status code | Error condition | Required action |
|---|---|---|
| 409 | Channel invalidated | Close the channel, call openChannel, resume from the last committed offset |
| 429 | Throttling | Exponential back-off and retry |
| 500 / 503 | Service error | Typically retryable after a short delay |
| 401 | Unauthorized | Verify credentials, JWT configuration, or role permissions |
Separate InvalidChannelException (HTTP 409), which affects only one channel and is fixed by reopening it, from InvalidClientException. The second means the whole SnowflakeStreamingIngestClient is compromised: close it, create a new client from the factory, and reopen all of its channels.
Checkpoint 2 of 8· Put it in order
Put the recovery steps for an HTTP 409 (channel invalidated) in order
- 1.Close the current channel
- 2.Resume from the last committed offset
- 3.Call openChannel to create a new instance
The old channel is no longer valid, so replace it, then continue from the last committed offset so no rows are lost or sent twice.
“Close the current channel, call openChannel to create a new instance, and then resume from the last committed offset.”Source: docs.snowflake.com
Checkpoint 3 of 8· Check yourself
Ingestion starts returning HTTP 403 after someone changes a role's grants. What is the correct response?
401 and 403 are authorization failures. A reopened channel or a new client would fail the same way, so fix the security configuration first.
“When an ingestion attempt results in an HTTP authorization error, you must correct the underlying permission or credential issue.”Source: docs.snowflake.com
Checkpoint 4 of 8· Exam question
A nightly batch load ingests large files where a small number of genuinely corrupt files exist among otherwise clean files. The team wants a corrupt file skipped only once its error rate crosses a meaningful threshold, rather than skipping a whole file over a single bad row or aborting the entire load. Which `ON_ERROR` configuration matches this requirement?
Correct answer: A — Use `ON_ERROR = SKIP_FILE_10%`, which skips a file only once the count of erroring rows exceeds ten percent of the rows in that file, leaving files with a low error rate loaded.
- A. This is correct. `SKIP_FILE_<num>%` sets a percentage error-rate threshold, and the file is only skipped once the proportion of erroring rows in it crosses that percentage, which is exactly the threshold-based skip behavior the team wants.
- B. Plain `SKIP_FILE` skips the entire file after the very first error, with no threshold at all, so it would discard files that only have one bad row among thousands of good ones — too aggressive for this requirement.
- C. `CONTINUE` never skips a file regardless of how many rows in it fail, so a file that is almost entirely corrupt would still have its clean rows loaded with no file-level cutoff, which does not give the team a skip threshold.
- D. `ABORT_STATEMENT` terminates the whole multi-file COPY operation at the first error anywhere, so it never reaches a per-file error-rate evaluation and cannot express a percentage threshold.
Sources3
3.Error tables: recovering the rejected rows
Channel status gives you counts and the most recent error message, not the rows that were rejected. To get those rows in the high-performance architecture, set ERROR_LOGGING on the target table. Valid rows keep loading. Rows that fail server-side parsing or transformation go to a dedicated error table, which also collects DML errors for that table. The error table stores the original payload sent to the API or SDK, before any pipe transformation. Even fields the pipe would drop are kept.
-- For a new table:
CREATE TABLE my_streaming_table (...) ERROR_LOGGING = TRUE;
-- For an existing table:
ALTER TABLE my_streaming_table SET ERROR_LOGGING = TRUE;Checkpoint 5 of 8· Fill the gap
Which table function returns the rejected streaming rows for a base table?
SELECT * FROM ? (my_streaming_table)
WHERE error_metadata:service = 'snowpipe_streaming';ERROR_TABLE reads a base table's error table. Filtering on error_metadata:service = 'snowpipe_streaming' separates streaming errors from DML errors.
Source: docs.snowflake.comEach error row has a timestamp, query_id, error_code, error_metadata and error_data. error_data:$1 holds the raw payload. error_metadata:details names the pipe_name and channel_name, gives offset_token_lower_bound and offset_token_upper_bound, and flags error_data_truncated for payloads larger than 128 MB. The error_data_content_type field tells you which kind of failure occurred and how to fix it:
- Valid JSON with a logical error. A NOT NULL column is missing, a value fails type conversion ("abc" into a NUMBER, for example), or a pipe transformation fails (such as division by zero). Read error_metadata:error_message and error_metadata:error_source, parse with PARSE_JSON(error_data:$1), correct the data, and reinsert it. - Syntactically invalid JSON. Read the syntax error in error_metadata:error_message, correct the payload, and reinsert it. - Invalid UTF-8. The payload is stored base64-encoded. This usually means a format or encoding problem in the upstream source. Decode it to find the bad bytes.
INSERT INTO my_streaming_table (col1, col2, col3)
SELECT
TRY_CAST(PARSE_JSON(error_data:"$1"):col1 AS NUMBER),
PARSE_JSON(error_data:"$1"):col2::STRING,
TRY_CAST(PARSE_JSON(error_data:"$1"):col3 AS TIMESTAMP)
FROM ERROR_TABLE(my_streaming_table)
WHERE error_metadata:service = 'snowpipe_streaming'
AND error_metadata:details:error_data_content_type = 'json'
AND timestamp >= DATEADD(hour, -24, CURRENT_TIMESTAMP());Checkpoint 6 of 8· Match them up
Match each error-table failure type to its resolution
Tap a term, then the definition that fits it.
Only the invalid UTF-8 case stores the payload base64-encoded. The two JSON cases keep a readable payload you can correct directly.
“Decode the payload stored in error_data:$1 with the BASE64_DECODE_STRING function to inspect the raw bytes and identify incorrect UTF-8 sequences.”Source: docs.snowflake.com
Error tables have limits. They only capture errors from server-side parsing and transformation. SDK validation errors, API failures and other asynchronous server-side errors never reach them, so watch those with getChannelStatus(). A high failure rate can increase latency. Routing rows to the error table adds no ingestion charge, but the stored data is billed at standard storage rates. After reprocessing, TRUNCATE ERROR_TABLE(my_streaming_table) clears the error table. Error tables are available only in the high-performance architecture. In the classic architecture, errors are handled client-side through the SDK.
Checkpoint 7 of 8· Check yourself
ERROR_LOGGING is on, but some failures never appear in ERROR_TABLE. Which kind of failure is expected to be missing?
Error tables only capture errors from server-side parsing and transformation. SDK validation errors happen before that stage, so you monitor them through getChannelStatus().
“Errors from other stages (SDK validation, API failures, and other server-side asynchronous errors) aren’t captured in error tables.”Source: docs.snowflake.com
Checkpoint 8 of 8· Exam question
Which statement correctly describes the default `ON_ERROR` behavior when no `ON_ERROR` option is specified?
Correct answer: A — Bulk loading with `COPY INTO` defaults to `ABORT_STATEMENT`, while continuous loading through Snowpipe defaults to `SKIP_FILE` for the same unspecified option.
- A. This is correct. Bulk `COPY INTO` statements default to `ABORT_STATEMENT`, stopping the whole file's load on the first error, while Snowpipe defaults to `SKIP_FILE`, skipping only the offending file so continuous ingestion keeps flowing.
- B. This reverses the actual defaults. Bulk loads do not silently continue past errors by default, and Snowpipe does not abort an entire continuous pipeline over one bad file; the real defaults are the opposite of what this option states.
- C. Bulk `COPY INTO` does not default to `SKIP_FILE`; its unspecified default is `ABORT_STATEMENT`, so this option is only half right for Snowpipe and wrong for bulk loading, meaning the two mechanisms do not share the same default.
- D. Snowpipe does not default to `ABORT_STATEMENT`; a continuous pipeline that aborted on every bad file would be impractical, which is why Snowpipe's unspecified default is `SKIP_FILE` instead.
Sources2
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Any Snowpipe Streaming error can be fixed by closing and reopening the channel.Why is that wrong?
For 401 and 403 authorization errors you must fix the permission or credential problem first. A reopened channel would fail the same way.
2.Because the high-performance architecture runs with ON_ERROR=CONTINUE, bad data can never invalidate a channel.Why is that wrong?
A schema evolution failure caused by user error, such as column names that can't be mapped, invalidates the channel even under CONTINUE.
3.Setting ERROR_LOGGING on a table fed by the classic Snowpipe Streaming SDK captures its rejected rows.Why is that wrong?
Error tables exist only in the high-performance architecture. In the classic architecture, errors are handled client-side through the SDK.
Covered in Error tables: recovering the rejected rows
Practise it for real
Capture, inspect and repair rejected Snowpipe Streaming rows using an error table
1.Run ALTER TABLE my_streaming_table SET ERROR_LOGGING = TRUE; on a table loaded through the high-performance architecture.
Why: Rows that fail server-side processing are only kept once error logging is on.
You should see: The statement succeeds, and valid rows keep loading as before.
2.Query SELECT * FROM ERROR_TABLE(my_streaming_table) WHERE error_metadata:service = 'snowpipe_streaming';
Why: This separates streaming failures from DML errors in the same error table.
You should see: One row per rejected row, with error_metadata and the raw payload in error_data:$1.
3.Group the errors by error_code and error_metadata:error_message over the last 24 hours.
Why: Counting by error type shows whether one upstream fault is causing most of the failures.
You should see: A short list of error messages ranked by count.
4.Run the INSERT … SELECT with PARSE_JSON and TRY_CAST for rows whose error_data_content_type is 'json'.
Why: Payloads that are valid JSON can be corrected and reinserted with plain SQL.
You should see: The corrected rows appear in my_streaming_table.
5.Run TRUNCATE ERROR_TABLE(my_streaming_table);
Why: Stored error rows are billed at standard storage rates, so clear them once they have been reprocessed.
You should see: ERROR_TABLE(my_streaming_table) returns no rows.
Stuck? Get a nudge
If a payload is base64-encoded, decode it with BASE64_DECODE_STRING before trying to fix it.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.https://docs.snowflake.com/en/user-guide/snowpipe-streaming/snowpipe-streaming-classic-overviewOfficial docs
“For a given batch of rows, the API supports the equivalent of ON_ERROR = CONTINUE | SKIP_BATCH | ABORT.”
↩︎ How bad rows are handled in each architecture“ABORT (default setting): Abort the entire batch of rows and throw an exception when the first error is encountered.”
↩︎ Checkpoint - 2.https://docs.snowflake.com/en/user-guide/snowpipe-streaming/snowpipe-streaming-error-tablesOfficial docs
“The high-performance architecture implicitly operates in ON_ERROR = CONTINUE mode, meaning valid rows are ingested while problematic rows are skipped.”
↩︎ How bad rows are handled in each architecture“The data stored in error tables is the original payload sent to the API or SDK before any pipe transformations are applied.”
↩︎ Error tables: recovering the rejected rows“Monitor server-side asynchronous errors using getChannelStatus().”
↩︎ Error tables: recovering the rejected rows“Error tables are available only for the Snowpipe Streaming high-performance architecture.”
↩︎ Exam trap 3“Decode the payload stored in error_data:$1 with the BASE64_DECODE_STRING function to inspect the raw bytes and identify incorrect UTF-8 sequences.”
↩︎ Checkpoint“Errors from other stages (SDK validation, API failures, and other server-side asynchronous errors) aren’t captured in error tables.”
↩︎ Checkpoint - 3.https://docs.snowflake.com/en/user-guide/snowpipe-streaming/snowpipe-streaming-high-performance-error-handlingOfficial docs
“A channel is considered invalid — and requires client action — if the channel_status_code in the channel status response is anything other than SUCCESS.”
↩︎ Channel status and choosing the right recovery“InvalidClientException: The entire SnowflakeStreamingIngestClient is compromised.”
↩︎ Channel status and choosing the right recovery“you must correct the underlying permission or credential issue.”
↩︎ Exam trap 1“the channel is invalidated if it encounters a schema evolution failure caused by user errors.”
↩︎ Exam trap 2“The Snowpipe Streaming SDK doesn’t automatically reopen the channel.”
↩︎ Prediction“Close the current channel, call openChannel to create a new instance, and then resume from the last committed offset.”
↩︎ Checkpoint“When an ingestion attempt results in an HTTP authorization error, you must correct the underlying permission or credential issue.”
↩︎ Checkpoint