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? # for a free GitHub account to open an issue and contact its maintainers and the community.

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

Already on GitHub? # 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.

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

No branches or pull requests

3 participants