How to fix PGRST103: Invalid range in Supabase
The PGRST103 error (HTTP 416: Range Not Satisfiable) occurs when you request a data range using .range() or pagination parameters that exceeds the available rows in your Supabase table. This guide walks through validating pagination bounds, fetching total counts before querying, and handling out-of-bounds requests gracefully.
Quick fix
Fixes PGRST103
const pageSize = 10;
const page = 5; // user clicks page 5
// Calculate offset (0-based)
const offset = pageSize * (page - 1); // 40
// Validate before range()
if (offset < 0 || pageSize <= 0) {
console.error("Invalid pagination parameters");
return;
}
const { data, error } = await supabase
.from('your_table')
.select('*')
.order('id')
.range(offset, offset + pageSize - 1);
if (error?.code === 'PGRST103') {
// Offset exceeds available rows
console.error("Requested page is beyond available data");
}- Verify your offset and limit values are positive and reasonable
- Fetch the total count before paginating
- Always add an .order() clause before .range()
- Handle PGRST103 gracefully by returning empty results
const pageSize = 10;const page = 5; // user clicks page 5// Calculate offset (0-based)const offset = pageSize * (page - 1); // 40// Validate before range()if (offset < 0 || pageSize <= 0) {console.error("Invalid pagination parameters");return;}const { data, error } = await supabase.from('your_table').select('*').order('id').range(offset, offset + pageSize - 1);if (error?.code === 'PGRST103') {// Offset exceeds available rowsconsole.error("Requested page is beyond available data");}PGRST103: Invalid range
What this error means
PostgREST enforces HTTP Range semantics before executing your query. When you use the `.range()` method or supply `Range` headers to paginate through results, PostgREST validates that the requested offset and limit fall within the dataset. If the offset exceeds the total number of rows, or if you request a negative limit, PostgREST returns HTTP 416 (Range Not Satisfiable) with a PGRST103 error body. This is a client-side input validation problem: your pagination parameters do not match the available data, so the server refuses to serve that unsatisfiable range.
Typical symptoms
- Pagination queries suddenly return { code: "PGRST103", message: "HTTP Range error" } with HTTP 416 status even though your table has data.
- The error happens when requesting a range at the end of your dataset (e.g., page 100 when only 50 pages exist).
- API calls with .range(offset, limit) work for the first few pages but fail when offset grows beyond the table row count.
- Negative limit values in your .range() call trigger PGRST103 instead of returning data.
- An offset of 1000 with only 100 rows in the table returns PGRST103 with details: "An offset of 1000 was requested, but there are only 100 rows."
Common causes
- The offset parameter exceeds the total number of rows in the result set (e.g., trying to fetch page 1000 when only 10 pages exist).
- The limit parameter is negative or zero, which PostgREST treats as an invalid range.
- Pagination logic does not check the total count before issuing .range() queries, so it blindly requests out-of-bounds pages.
- Data was deleted between fetching the count and issuing the range query, causing the previously-valid offset to exceed the new row count.
- The order of query conditions changed (e.g., adding a .where() filter reduced rows), making a previously-valid range out of bounds.
How to fix "PGRST103: Invalid range"
Before calling .range(), confirm that both the offset and limit are positive integers and that the sum does not exceed your expected row count:
const pageSize = 10;
const page = 5; // user clicks page 5
// Calculate offset (0-based)
const offset = pageSize * (page - 1); // 40
// Validate before range()
if (offset < 0 || pageSize <= 0) {
console.error("Invalid pagination parameters");
return;
}
const { data, error } = await supabase
.from('your_table')
.select('*')
.order('id')
.range(offset, offset + pageSize - 1);
if (error?.code === 'PGRST103') {
// Offset exceeds available rows
console.error("Requested page is beyond available data");
}The range(from, to) method is 0-based and inclusive, so .range(0, 9) fetches 10 rows.
Use count: 'exact' to get the total number of rows before issuing range queries. This lets you validate the offset against a known total:
const { count, error: countError } = await supabase
.from('your_table')
.select('*', { count: 'exact', head: true })
.eq('status', 'active'); // Apply same filters as your data query
if (countError) {
console.error("Failed to fetch count:", countError);
return;
}
console.log(`Total rows: ${count}`);
// Now calculate safe pagination
const pageSize = 10;
const maxPage = Math.ceil(count / pageSize);
const requestedPage = Math.min(page, maxPage); // Clamp to valid range
const offset = pageSize * (requestedPage - 1);
const limit = Math.min(pageSize, count - offset);
const { data } = await supabase
.from('your_table')
.select('*')
.eq('status', 'active') // Same filter as count
.order('id')
.range(offset, offset + limit - 1);This prevents ever requesting a range beyond the available data.
Without an explicit order, the range behavior is undefined and can change if your data or indexes shift. Always add a deterministic order clause:
const { data } = await supabase
.from('your_table')
.select('*')
.order('id', { ascending: true }) // Explicit order
.range(0, 9);This ensures rows are returned in a consistent order and pagination is predictable.
Wrap range queries in error handling so out-of-bounds requests do not crash your UI:
async function fetchPage(page, pageSize) {
try {
const offset = pageSize * (page - 1);
const { data, error, status } = await supabase
.from('your_table')
.select('*')
.order('id')
.range(offset, offset + pageSize - 1);
if (status === 416 || error?.code === 'PGRST103') {
// Out of bounds, return empty array
console.warn(`Page ${page} is beyond available data`);
return [];
}
if (error) throw error;
return data;
} catch (err) {
console.error("Fetch error:", err.message);
throw err;
}
}This prevents 416 errors from breaking user experience when pagination parameters are invalid.
The PGRST103 error message was truncated in older PostgREST versions (particularly when using Prefer: count=exact). If you are running PostgREST 10.x, upgrade to 12.x or later for complete error details and better RFC 7233 Range compliance:
# Supabase Cloud upgrades automatically
# Self-hosted: check your PostgREST version
psql -d your_db -c "SELECT version();"
# Update PostgREST container or binary to 12.x+Newer versions include fixes for edge cases like when offset equals the total row count.
Advanced notes
PGRST103 is part of PostgREST's HTTP Range validation (error group 3) that enforces RFC 7233 semantics before any SQL runs. The error is deterministic: if your offset exceeds the row count, PostgREST always rejects the range. This is different from a 500 or query timeout—it means your pagination parameters are mathematically invalid. For production apps, always fetch count and clamp pagination values to [0, max_page). In edge cases where data changes rapidly, a count-then-query pattern can still produce stale results if rows are deleted between the count and the range query, so idempotent offset logic (e.g., clamping to max_page) is safer than throwing errors.
Frequently asked questions
- What does "PGRST103" mean?
- PostgREST enforces HTTP Range semantics before executing your query. range() method or supply Range headers to paginate through results, PostgREST validates that the requested offset and limit fall within the dataset. If the offset exceeds the total number of rows, or if you request a negative limit, PostgREST returns HTTP 416 (Range Not Satisfiable) with a PGRST103 error body.
- How do I fix "PGRST103"?
- The PGRST103 error (HTTP 416: Range Not Satisfiable) occurs when you request a data range using .range() or pagination parameters that exceeds the available rows in your Supabase table. This guide walks through validating pagination bounds, fetching total counts before querying, and handling out-of-bounds requests gracefully.
- What causes "PGRST103"?
- The offset parameter exceeds the total number of rows in the result set (e.g., trying to fetch page 1000 when only 10 pages exist). The limit parameter is negative or zero, which PostgREST treats as an invalid range. Pagination logic does not check the total count before issuing .range() queries, so it blindly requests out-of-bounds pages.
- Is the "PGRST103" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚Supabase Official Documentation
- 📖docs.postgrest.org - errors(Official Docs)
- 📖supabase.com - range(Official Docs)
- 🐙github.com - 2470(GitHub)
- 🐙github.com - 2824(GitHub)