Amazon Redshift Query Errors: Fix COPY, Permission, Timeout and Disk Full Issues

This page covers the Redshift errors that break pipelines and block analysts, and shows you exactly where to look to fix them. You will find step-by-step diagnosis for relation does not exist, permission denied, COPY load failures, zero-row loads, query timeouts, and disk full errors caused by data skew.

If your Redshift query just failed and you need to know what went wrong, start with the quick triage table below. If you are new to Redshift, the Amazon Redshift Overview explains the fundamentals before you start troubleshooting.

Redshift is not a standard relational database. It is a distributed columnar store, which means its failure modes and diagnostic tools work differently from PostgreSQL or MySQL. Most errors have a clear root cause once you know which system view to query.

Simple explanation

Almost every Redshift operational error fits into one of five buckets:

  • Object lookup problems — the query references a table or schema the session cannot find, usually because of a search_path mismatch
  • Permission problems — the user or IAM role has not been granted the privilege to access the object
  • Load or parse failures — the COPY command encountered rows that do not match the expected format, delimiter, or data type
  • Workload and timeout issues — the query exceeded a time limit set by Workload Manager or by a session parameter
  • Storage and design issues — one node is holding a disproportionate share of the data, causing it to fill faster than the others
How to think about it

Think of Redshift like a large office building with multiple floors. Each floor is a node, and each floor has its own filing system (schema). If you walk up to the front desk and ask for “the orders file,” they will only search the floor you are on. If the file is on the third floor and you are on the first, you get “relation does not exist” — not because the file is missing, but because no one told the desk which floor to check. Most Redshift errors work like this: something exists, but the session is not pointed at the right place.

Once you match your error to one of the five buckets, you know which system table to query and what kind of fix to apply.

How Redshift troubleshooting works

The standard diagnostic flow:

  1. Identify the exact error text. Copy the full error message. The exact wording tells you which category you are in.
  2. Get the query ID. Use SELECT pg_last_query_id() for the most recent query, or find it in STL_QUERY by matching the query text and timestamp.
  3. Query the relevant system view. Each error type has a corresponding view: STL_LOAD_ERRORS for COPY failures, STL_WLM_QUERY for timeouts, SVV_TABLE_INFO for skew and disk.
  4. Confirm the root cause. The system views contain far more detail than the error message alone: file names, line numbers, column values, queue settings, and node-level storage figures.
  5. Apply the fix. Most fixes are either a schema or privilege change, a data correction, a COPY option adjustment, or a table redesign.
System view access

Many STL, STV, and SVL views show each user only their own rows by default. Superusers and users with the SYS:DBA role can see all users’ rows. Newer SYS monitoring views — such as SYS_QUERY_HISTORY and SYS_LOAD_ERROR_DETAIL — are available to all users for their own queries without superuser access. This page uses STL/SVL views in examples because they are the standard low-level source, but equivalent SYS views exist for many of them.

Primary diagnostic views:

ViewWhat it shows
STL_LOAD_ERRORSRow-level parse failures from COPY
STL_LOAD_COMMITSFiles scanned during COPY (reveals zero-row loads)
STL_QUERYQuery history with status and row counts
STL_WLM_QUERYWLM queue assignment and timeout per query
SVL_QUERY_REPORTPer-segment execution detail for a query
SVV_TABLE_INFOTable size, skew ratio, and sort key stats
SVV_DISKUSAGEDisk usage by table and node

Quick triage: start with the symptom

Symptom / errorLikely causeFirst thing to checkView or command
relation "x" does not existTable in wrong schema or search_path not setSELECT schemaname, tablename FROM pg_tables WHERE tablename = 'x'pg_tables
permission denied for relation xNo SELECT or USAGE privilege grantedCheck grants on table and schemaGRANT, pg_table_def
COPY command fails with errorBad data format, wrong delimiter, encoding issue, type mismatchCheck row-level failuresSTL_LOAD_ERRORS
COPY succeeded, zero rows loadedS3 path prefix matches no objectsCheck which files COPY actually scannedSTL_LOAD_COMMITS, aws s3 ls
Query cancelled / timeoutstatement_timeout or WLM queue timeout exceededCheck session setting and WLM queueSHOW statement_timeout, STL_WLM_QUERY
Disk full on one nodeData skew from poor distribution keyCheck skew ratio per tableSVV_TABLE_INFO
Get the query ID fast

After any failed query or COPY, run SELECT pg_last_query_id() immediately. This ID is your anchor for every system view lookup. Write it down before running another query — it gets overwritten each time.

Error: relation does not exist

ERROR: relation "orders" does not exist

This error means Redshift cannot find a table named orders in the schemas the current session is searching. The table almost certainly exists. It is just not where the session is looking.

Analogy

Redshift’s search_path works like a library card catalogue that only covers certain floors. If your table is on floor 3 (schema: sales) and your session’s catalogue only lists floors 1 and 2, the table is invisible — not missing. Setting search_path is like telling the librarian which floors to check.

Check which schema the table is actually in:

SELECT schemaname, tablename
FROM pg_tables
WHERE tablename = 'orders';

If this returns a row, the table exists. The session’s search_path does not include that schema. Fix it for the current session:

SET search_path TO sales, public;

Or qualify the table name permanently in your queries:

SELECT * FROM sales.orders;

If pg_tables returns nothing, the table does not exist. Check for typos or partial matches:

SELECT schemaname, tablename, tableowner
FROM pg_tables
WHERE tablename ILIKE '%order%';

A scheduled job that creates the table may have failed, or the table was dropped.

Case sensitivity: Redshift folds unquoted identifiers to lowercase at parse time. If the table was created with a quoted mixed-case name such as CREATE TABLE "Orders", you must reference it the same way: SELECT * FROM "Orders". Without quotes, Redshift searches for orders (lowercase) and finds nothing.

Concurrency edge case: In rare cases a session running under snapshot isolation may not see a table created by a concurrent transaction that committed after the snapshot was taken. If this appears in an automated pipeline, check transaction isolation settings.

If you are still learning how Redshift organises schemas and clusters, the Redshift architecture guide covers how schemas, nodes, and slices relate to each other.

Error: permission denied for relation

ERROR: permission denied for relation orders

The current user has no SELECT privilege on the table. Redshift requires explicit grants at both the schema level (USAGE) and the table level (SELECT).

Grant access to a specific user:

-- Schema usage first
GRANT USAGE ON SCHEMA sales TO analyst_user;

-- Then table access
GRANT SELECT ON TABLE sales.orders TO analyst_user;

Grant access to all existing tables in a schema:

GRANT USAGE ON SCHEMA sales TO GROUP analysts;
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO GROUP analysts;

Cover tables created in the future:

GRANT SELECT ON ALL TABLES IN SCHEMA applies only to tables that exist at the time of the grant. Tables added later are not included. Use a default privilege to handle future tables:

ALTER DEFAULT PRIVILEGES IN SCHEMA sales
GRANT SELECT ON TABLES TO GROUP analysts;
COPY permission errors work differently

If the permission error is happening during a COPY command rather than a SELECT, the problem is usually the IAM role attached to Redshift, not a SQL privilege. The IAM role needs s3:GetObject and s3:ListBucket on the source bucket. SQL GRANT statements will not fix an IAM role gap. See the IAM access denied errors guide for role debugging steps.

COPY errors: bad rows, format mismatches, and failed loads

The COPY command loads data from S3 into Redshift. By default it aborts on the first row that fails validation. The top-level error message is usually generic. The actual detail is in STL_LOAD_ERRORS.

Re-run COPY with MAXERROR to collect all failures:

COPY sales.orders
FROM 's3://my-data-bucket/orders/2026/05/13/'
IAM_ROLE 'arn:aws:iam::111122223333:role/redshift-s3-role'
CSV
DELIMITER ','
IGNOREHEADER 1
MAXERROR 100;

With MAXERROR 100, COPY continues loading and accepts up to 100 row failures before aborting. After it completes, inspect the failures:

SELECT
    filename,
    line_number,
    colname,
    type,
    raw_line,
    raw_field_value,
    err_reason
FROM STL_LOAD_ERRORS
WHERE query = pg_last_copy_id()
ORDER BY line_number;

Common failure reasons and what to do:

err_reasonCauseFix
Invalid digit, Value 'abc', Pos 0String value in a numeric columnFix source data or check delimiter alignment
String length exceeds DDL lengthValue longer than VARCHAR columnIncrease column size in DDL or truncate at source
Delimiter not foundWrong delimiter in COPY optionsConfirm the actual delimiter and update COPY
Invalid Date FormatDate format in data does not match expected formatAdd DATEFORMAT 'auto' or specify the format explicitly
Encoding mismatchFile has Windows line endings or BOMConvert to UTF-8, strip BOM, or use ACCEPTINVCHARS

Fix validation errors in the source data. Fix format mismatches by adjusting COPY options. Do not use ACCEPTANYDATE or IGNOREBLANKLINES as blanket suppressors without understanding what data you are silently discarding.

If your data arrives via AWS Glue before loading into Redshift, the AWS Glue overview explains how Glue handles schema inference and format conversion. The loading data into Redshift guide covers COPY command options in full.

COPY succeeded but loaded 0 rows

Silent failure

COPY returns success even when it finds no files at the specified S3 path. There is no error to chase. The command simply does nothing and reports it finished. This trips up experienced engineers, not just beginners. Always verify row counts after a COPY that feeds a critical pipeline.

Confirm what COPY actually scanned:

SELECT
    filename,
    lines_scanned,
    bytes_scanned
FROM STL_LOAD_COMMITS
WHERE query = pg_last_copy_id();

If this returns no rows, COPY found no files matching the prefix. The path in your COPY statement does not match the actual S3 object keys.

Verify the S3 path directly:

# Check what is actually at the path
aws s3 ls s3://my-data-bucket/orders/2026/05/13/

# If empty, scan nearby paths to find the actual location
aws s3 ls s3://my-data-bucket/orders/ --recursive | head -20

Common path mismatches:

  • Files are at orders/2026-05-13/ but COPY references orders/2026/05/13/
  • Files use a different naming convention than the pipeline expects
  • The upstream job failed or has not finished writing files yet
  • The S3 bucket is in a different region than the Redshift cluster

IAM role check: The IAM role attached to Redshift needs s3:ListBucket on the bucket, not just s3:GetObject, to scan a prefix. Without s3:ListBucket, COPY may silently find no files instead of raising an access error. Confirm both permissions are present.

Empty files: If STL_LOAD_COMMITS shows files were scanned but lines_scanned is 0, the files exist but are empty. Trace back to the upstream job that wrote them.

For a broader look at how S3 paths, prefixes, and bucket policies interact, see the S3 overview.

Query cancelled: statement_timeout vs WLM timeout

Redshift has two separate timeout mechanisms and beginners often confuse them. They are independent, serve different purposes, and are diagnosed differently.

Analogy

statement_timeout is like a personal alarm on your watch. It fires for you regardless of where you are or what queue you are in. WLM timeout is like a closing-time announcement for the whole store — it affects everyone in that queue at once. You can hit your watch alarm before the store closes, the store can close before your alarm rings, or both can fire at the same time. They are completely separate clocks.

statement_timeout is a session-level or user-level parameter. It cancels any query that runs longer than the specified number of milliseconds, regardless of which WLM queue the query is in.

-- Check the current session setting
SHOW statement_timeout;

-- Set a timeout for the current session (in milliseconds)
SET statement_timeout TO 60000;  -- 60 seconds

-- Set a default for a specific user
ALTER USER analyst_user SET statement_timeout TO 120000;

WLM timeout is configured per queue in Workload Manager. It applies only to queries admitted to that queue and is enforced by the WLM service independently of statement_timeout.

-- Find the WLM queue and elapsed time for a specific query
SELECT
    query,
    service_class,
    elapsed_time / 1000000 AS elapsed_seconds,
    queue_elapsed_time / 1000000 AS time_in_queue_seconds,
    status
FROM STL_WLM_QUERY
WHERE query = 12345;  -- your query ID

The service_class column maps to a WLM queue. Values of 5 and above typically indicate user-defined queues. If elapsed_seconds is close to the queue timeout limit, WLM is the likely culprit.

Which timeout fired? Check SHOW statement_timeout first. If it is 0 (no limit), the cancellation came from WLM. See the comparison table in the statement_timeout vs WLM section for the full breakdown.

What to do:

  • If the query is genuinely slow, optimise it before raising the timeout. See the Redshift performance optimisation guide for sort key, join, and distribution fixes.
  • If the query is correct but runs in a queue with an aggressive timeout, move it to a queue with a longer limit by adjusting WLM routing rules.
  • If statement_timeout is too low for interactive use, raise it at the user level rather than disabling it globally.

Disk full, skew, and table design issues

ERROR: Disk Full Detail: -----------------
node: 0, disk: /dev/xvdc, capacity: 200000000KB, used: 198000000KB

Redshift distributes rows across nodes according to the table’s distribution style and key. If the distribution key has low cardinality, or if one value dominates the data, most rows land on the same node while others sit mostly empty. That one node fills up first and triggers disk full errors even though the cluster as a whole still has available space.

Analogy

Imagine a supermarket with 8 checkout registers. A poor distribution key is like routing all customers whose surname starts with A-F to register 1, while registers 2-8 are nearly empty. Register 1 gets overwhelmed and jams — not because the supermarket ran out of capacity, but because the routing was uneven. Changing the distribution key is like re-routing customers by last digit of their loyalty card number instead, spreading the load evenly across all registers.

How to confirm skew:

SELECT
    schema,
    "table",
    size AS size_mb,
    skew_rows,
    pct_used
FROM SVV_TABLE_INFO
WHERE schema = 'sales'
ORDER BY skew_rows DESC;

skew_rows ranges from 0 (perfectly even) to 1 (worst possible skew). A value above 0.4 indicates significant imbalance. A value near 1.0 means one node holds almost all the data.

Check the current distribution key:

SELECT "column", distkey
FROM pg_table_def
WHERE tablename = 'orders'
  AND schemaname = 'sales';

Fix skew by rebuilding with a better key:

A good distribution key has high cardinality and appears frequently in JOIN conditions. If customer_id = 1 accounts for 60% of rows, it is a poor distribution key. order_id (unique per row) distributes far more evenly.

-- Rebuild with a better distribution key
CREATE TABLE sales.orders_new
DISTSTYLE KEY
DISTKEY (order_id)
SORTKEY (order_date)
AS SELECT * FROM sales.orders;

-- Rename when ready
ALTER TABLE sales.orders RENAME TO orders_old;
ALTER TABLE sales.orders_new RENAME TO orders;
Rebuilding locks the table

The CTAS rebuild approach above creates a new table, not an in-place modification. Plan for the window when both versions exist and extra disk space is temporarily consumed. Run this during a maintenance window if the table is large or actively queried.

Reclaiming space: Redshift uses soft deletes. Deleted rows remain on disk until VACUUM runs. Redshift performs automatic background VACUUM, which handles most cases without intervention. After very large deletes or table rebuilds, you can run VACUUM DELETE ONLY table_name manually if you need space reclaimed immediately. Do not build this into routine pipelines.

For a full explanation of distribution styles, sort keys, and their cost implications, see the Redshift cost optimisation guide.

When to use this guide

This page covers first-pass diagnosis of common Redshift operational and query errors: things that fail outright, load nothing, or get cancelled. Use it when a query throws an error, a COPY job produces unexpected results, or a node runs out of disk space.

If your queries complete without errors but run too slowly, that is a performance problem rather than an error. Move to the Redshift performance optimisation guide for that path.

If you are comparing Redshift to another AWS database service and deciding which to use, the Redshift vs RDS comparison covers the key differences in architecture and use case.

Common mistakes

  1. Reading only the top-level error and skipping system views. The message Redshift surfaces to the client is often generic. STL_LOAD_ERRORS, STL_WLM_QUERY, and SVV_TABLE_INFO contain the actual detail: bad values, queue assignments, skew ratios. Always check the system view before deciding on a fix.
  2. Assuming Redshift behaves like PostgreSQL. Redshift is derived from an old version of PostgreSQL but diverges significantly in functions, system tables, and supported features. Something that works in Postgres may fail or behave differently in Redshift. The Redshift overview covers the most important differences.
  3. Confusing statement_timeout with WLM timeout. They are separate mechanisms. If statement_timeout is 0 (disabled) and a query still gets cancelled, WLM timeout is the cause. Check STL_WLM_QUERY to confirm which queue fired the cancellation.
  4. Assuming COPY found files because the command succeeded. COPY returns success even when no files match the S3 prefix. Zero rows loaded with no error is a path mismatch problem, not a success. Always verify with STL_LOAD_COMMITS.
  5. Choosing a distribution key by column name rather than by data distribution. A column named “customer_id” sounds like a good key, but if one customer accounts for the majority of your data it causes severe skew. Check actual value distribution before choosing a distkey.
  6. Using VACUUM as a routine pipeline step. Redshift runs background VACUUM automatically. Calling VACUUM manually in every pipeline adds overhead and conflicts with automatic maintenance. Reserve manual VACUUM for cases where you need space reclaimed immediately after a very large delete.

Redshift statement_timeout vs WLM timeout

statement_timeoutWLM timeout
What it isA session or user parameter that limits query run timeA per-queue setting in Workload Manager
Default0 (no limit)Set per queue in WLM configuration
ScopeAny query in the session, regardless of WLM queueOnly queries in a specific WLM queue
Where it is setSET statement_timeout TO ms or ALTER USER ... SETWLM queue config in the console or parameter group
How to checkSHOW statement_timeoutSTL_WLM_QUERY — check elapsed_time vs queue limit
Error message”canceling statement due to statement timeout""Query cancelled on user’s request or due to timeout”
Right fixRaise the limit for the user, or optimise the queryMove query to a different queue, or optimise

Both mechanisms can be active simultaneously. Whichever fires first wins. statement_timeout protects individual sessions from runaway queries. WLM timeout enforces SLAs across a queue of competing workloads.

Summary

  • Match your error to one of five buckets (object lookup, permissions, load failure, timeout, or disk/skew), then query the relevant system view for the detail.
  • ”Relation does not exist” almost always means the table is in a schema not in the current search_path. Check pg_tables and qualify the table name.
  • COPY failures are diagnosed in STL_LOAD_ERRORS. Use MAXERROR to collect all failures before aborting. The top-level error alone is rarely enough.
  • COPY can return success with zero rows loaded. Always verify with STL_LOAD_COMMITS that files were actually scanned.
  • statement_timeout and WLM timeout are separate. Check SHOW statement_timeout first. If it is 0, the cancellation came from WLM.
  • Disk full errors on a single node point to data skew. Check SVV_TABLE_INFO and rebuild the table with a higher-cardinality distribution key.

Frequently asked questions

Why does Redshift say relation does not exist when the table is there?

The table exists in a schema not included in the current session's search_path. Run SELECT schemaname, tablename FROM pg_tables WHERE tablename = 'your_table' to find the right schema, then either qualify the name (SELECT * FROM schema.table) or set the path (SET search_path TO schema, public).

What should I check first after a COPY failure?

Query STL_LOAD_ERRORS using the query ID from pg_last_copy_id(). This table shows the exact file, line number, column, and reason for every row that failed — far more useful than the top-level COPY error message. Re-run COPY with MAXERROR set to a positive number so it collects all failures rather than aborting on the first one.

Why did COPY run but load zero rows?

COPY reports success even when it finds no files matching the S3 path prefix. Check STL_LOAD_COMMITS to see whether any files were scanned. If that returns nothing, the S3 path in your COPY statement does not match the actual object keys. Verify with aws s3 ls on the exact path. Also confirm the IAM role has s3:ListBucket permission, which is required to scan the prefix.

What is the difference between statement_timeout and WLM timeout in Redshift?

statement_timeout is a session-level or user-level parameter that cancels any query running longer than the specified number of milliseconds. WLM timeout is configured per queue in Workload Manager and applies to queries admitted to that queue. Both produce a cancellation, but they are diagnosed differently. Check SHOW statement_timeout for the session setting and STL_WLM_QUERY for the WLM queue timeout.

When should I troubleshoot query performance instead of query errors?

Once a query runs to completion without errors but returns results slowly, the problem shifts from correctness to performance. Move to the performance troubleshooting path if queries run but take minutes longer than expected, if SVL_QUERY_REPORT shows one segment consuming far more time than others, or if STL_ALERT_EVENT_LOG flags missing statistics or nested loop joins.

Last verified: 13 May 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.