storage
storage copied to clipboard
Failing RLS policy on bucket returns 400 (StorageUnknownError) instead of 403 (Access Denied)
Bug report
- [x] I confirm this is a bug with Supabase, not with my own application.
- [x] I confirm I have searched the Docs, GitHub Discussions, and Discord.
Describe the bug
When trying to check the existence of a file within a private storage bucket, if the RLS policy fails, a storage error of type StorageUknownError with HTTP status 400 is returned from the JavaScript client.
To Reproduce
Steps to reproduce the behavior, please provide code snippets or a repository:
- Create a private bucket with a simple failing RLS policy.
-- Create the bucket for assets
insert into storage.buckets(id, name, public)
values ('assets', 'assets', false);
-- Create a policy that will always fail
-- NOTE: Changing false to true here results in a successful response
create policy "Users can never access" on storage.objects
for select to public using (bucket_id = 'assets' and false);
- Apply the migration
- Seed it with an asset (optional)
- Try to access the bucket by checking the existence of such an asset. For example...
import { createClient } from "@supabase/supabase-js";
const main = async () => {
// Check for URL, anon key and service role key
if (!process.env.SUPABASE_URL) {
console.error("No SUPABASE_URL environment variable found. Exiting...");
process.exit(1);
}
if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
console.error(
"No SUPABASE_SERVICE_ROLE_KEY environment variable found. Exiting...",
);
process.exit(1);
}
if (!process.env.SUPABASE_ANON_KEY) {
console.error(
"No SUPABASE_ANON_KEY environment variable found. Exiting...",
);
process.exit(1);
}
const serviceClient = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY,
);
// Create a user
const DEFAULT_PASSWORD = "Developer123!";
const adminUserResponse = await serviceClient.auth.admin.createUser({
email: "[email protected]",
password: DEFAULT_PASSWORD,
email_confirm: true,
user_metadata: {
first_name: "Bill",
last_name: "Keys",
},
});
if (!adminUserResponse?.data.user) {
console.error(
"No user returned from Supabase signup. Error:",
adminUserResponse.error,
);
console.log("Exiting...");
process.exit(1);
}
const client = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_ANON_KEY,
);
const signIn = async () => {
const { data, error } = await client.auth.signInWithPassword({
email: "[email protected]",
password: "Developer123!",
});
};
await signIn();
const { data: exists, error: assetsError } = await client.storage.from(
"assets",
)
.exists(
"an-asset.webp",
);
console.log("(JWT client) Storage response error: ", assetsError);
};
main();
This should output something like this...
(JWT client) Storage response error: StorageUnknownError: {}
at <anonymous> (/Users/tomtitherington/Development/supa-asset-api/supabase/node_modules/.pnpm/@[email protected]/node_modules/@supabase/storage-js/src/lib/fetch.ts:36:12)
at Generator.next (<anonymous>)
at fulfilled (/Users/tomtitherington/Development/supa-asset-api/supabase/node_modules/.pnpm/@[email protected]/node_modules/@supabase/storage-js/dist/main/lib/fetch.js:5:58)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5) {
__isStorageError: true,
originalError: Response {
status: 400,
statusText: 'Bad Request',
headers: Headers {
'content-type': 'application/json; charset=utf-8',
'content-length': '69',
connection: 'close',
date: 'Thu, 06 Mar 2025 11:57:09 GMT',
'access-control-allow-origin': '*',
'x-kong-upstream-latency': '9',
'x-kong-proxy-latency': '0',
via: 'kong/2.8.1'
},
body: null,
bodyUsed: false,
ok: false,
redirected: false,
type: 'basic',
url: 'http://127.0.0.1:54321/storage/v1/object/assets/5a80860e-d075-4181-96f6-b0c075fcbc15.webp'
}
}
Expected behavior
I would expect an error of type "AccessDenied" or a HTTP status 403 to be returned as documented here.
System information
- OS: macOS
- Version of supabase-js: v2.49.1
- Version of Node.js: v23.7.0
Hey @tomtitherington thanks for reaching out.
Yes, the 400 status code was a bit of a legacy decision to have a single status code for errors, however, we are going to fix it in the next major version of Storage
Thanks for getting back @fenos. Is there a roadmap for releases or rough timeline for when this might be coming out?
This is effecting an app we have in production so would be great if you could share some details so we can plan around this.
Thanks!
Hey @tomtitherington no worries at all; it is my pleasure 👍
I don't have an exact timeline for this, but it is something we are actively changing for the next major release of Storage. In the meantime, i'd be happy to help handle this error
Could you explain further what the problem you are encountering when trying to handle this error
Closing this issue as it will be an upcoming initiative to improve the statusCodes returned