Skip to content

Renaming/deleting a folder in Supabase Studio Storage dashboard leaves a ghost record in storage.prefixes table #651

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
2 tasks
lhengl opened this issue Mar 18, 2025 · 2 comments
Labels
bug Something isn't working

Comments

@lhengl
Copy link

lhengl commented Mar 18, 2025

Bug report

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

In local development, using Supabase Studio (latest version), if you create a folder and then rename it, the old folder will remain, and a new folder is created instead. So you end up with 2 folders. When this happens, you can delete the new folder, but you can't delete the old folder. This means that the bucket can't be deleted either.

Additionally a related problem is when creating nested folders (at least 2 levels deep) with at least 2 folders in each level. The parent folder of these folders will not be deleted.

I think there may be more nuances to this problem, but this is the main ones that I can replicate.

The work around is to remove the record from the storage.prefixes table using SQL editor.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

To reproduce this behavior by renaming:

  1. Create a bucket called "Bucket 1"
  2. Create a folder inside "Bucket 1" called "Folder 1"
  3. Rename "Folder 1" to "Folder 2"
  4. Delete "Folder 2" (Folder 2 is deleted)
  5. Delete "Folder 1" (Folder 1 is not deleted)
  6. Try to delete "Bucket 1" (Will get an error)
  7. See Error

To reproduce this behavior by creating at least 2 levels deep of folder structure:

  1. Create a bucket called "Bucket 1"
  2. Create a folders in the following structure:
  • folder 1
    • folder 1-1
      • folder 1-1-1
      • folder 1-1-2
    • folder 1-2
      • folder 1-2-1
      • folder 1-2-2
  1. Delete "folder 1" (All folders will be deleted except folder 1)
  2. Try to delete "Bucket 1" (Will get an error)
  3. See Error

Expected behavior

  • Renaming a folder should remove the old folder and its record from the storage.prefixes table.
  • Deleting a root folder of nested folder should delete all records from the storage.prefixes table.

Screenshots

Image

System information

  • OS: macOS 13.7.4
  • Browser: Chrome Version 134.0.6998.89 (Official Build) (x86_64)
  • Version of supabase-js: N/A
  • Version of Node.js: N/A
  • Dashboard Version: Unsure how to get version but I'm using the latest version as of Marc 18 2025

Additional context

I came across this problem when I had complex folder structures, and I tried to delete the root folder, and noticed that alot of the folders were ghosted and I couldn't remove the bucket. I then noticed that those folders were still in the storage.prefixes table.

@lhengl lhengl added the bug Something isn't working label Mar 18, 2025
@catcoon33
Copy link

catcoon33 commented Mar 22, 2025

Hi @lhengl ,
In my local environment, I noticed that in the definition of the storage.objects table:

Image

This means that both insert and update operations trigger objects_insert_prefix_trigger, which inserts records into the prefixes table. However, when renaming a folder, the old record remains in the prefixes table, leading to a “ghost directory”.

Currently, my workaround is to introduce a new objects_update_prefix_trigger:

CREATE OR REPLACE FUNCTION storage.objects_update_prefix_trigger()  
RETURNS TRIGGER AS $$  

DECLARE  
    old_prefixes TEXT[];  

BEGIN  
    -- Ensure this is an update operation and the name has changed  
    IF TG_OP = 'UPDATE' AND NEW.name <> OLD.name THEN  

        -- Retrieve old prefixes  
        old_prefixes := storage.get_prefixes(OLD.name);  

        -- Remove old prefixes that are only used by this object  
        FOR i IN 1..array_length(old_prefixes, 1) LOOP  
            IF NOT EXISTS (  
                SELECT 1 FROM storage.objects  
                WHERE bucket_id = OLD.bucket_id  
                AND name <> OLD.name  
                AND name LIKE (old_prefixes[i] || '%')  
            ) THEN  
                DELETE FROM storage.prefixes  
                WHERE bucket_id = OLD.bucket_id AND name = old_prefixes[i];  
            END IF;  
        END LOOP;  

        -- Add new prefixes  
        PERFORM storage.add_prefixes(NEW.bucket_id, NEW.name);  
    END IF;  

    -- Set the new level  
    NEW.level := storage.get_level(NEW.name);  

    RETURN NEW;  
END;  

$$ LANGUAGE plpgsql;
-- Remove the old update trigger  
DROP TRIGGER IF EXISTS objects_update_create_prefix ON storage.objects;  

-- Create a new trigger to ensure it runs BEFORE UPDATE  
CREATE TRIGGER objects_update_create_prefix  
BEFORE UPDATE ON storage.objects  
FOR EACH ROW  
WHEN (NEW.name <> OLD.name)  
EXECUTE FUNCTION storage.objects_update_prefix_trigger();

However, I’m not entirely sure whether this modification might introduce other issues.

@w3b6x9 w3b6x9 transferred this issue from supabase/supabase Apr 3, 2025
@Hallidayo
Copy link

Hi all, I've moved this across due to a PR being opened in the storage repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants