My reddit thread on this

TL;DR: I messed up while importing a big library into immich and need to roll this back somehow.

I’ve a semi large immich setup (5tb) with 2 users. I mistakenly uploaded around 500GB of assets into other user’s account instead of my own account due to my messup of the API keys through immich-go.

I was wondering if there is a clean approach to fix the ownership issue instead of deleting everything and starting over. Apparently not

I thought about doing something like this but I was not sure if the physical files can stay under /upload/user2_id while owner is user1_id and what that would mean for other sub components for immich:

UPDATE "asset" SET "ownerId" = 'user1\_id' WHERE "ownerId" = 'user2\_id' AND "createdAt" >= '2025-08-26';

Apparently, it’s somewhat possible but not everything will work without additional work in the db and possibly moving physical files according to the reddit Immich community, huge thanks!

Then I had to go for safer path by deleting the assets from the other account then reupload. Surprise, this is not easy either. Most of the info online is outdated, I don’t see “Recently uploaded” button anymore in the UI; Date based search is not giving the exact images I uploaded. So what can i do?

Let’s pull the asset ids from the db into a file since i know how to do it:

docker exec immich_postgres psql -U $DB_USERNAME -d $DB_NAME -c "SELECT * FROM assets WHERE \"ownerId\" = '$OWNER_ID' AND \"createdAt\" >= '2025-08-26';" > asset_ids_to_be_deleted.txt

Then I need some python power to read this file and call immich asset deletion endpoint through the api. ofc you can do that from curl too

import requests
from datetime import datetime
# === CONFIG ===
IMMICH_URL = "http://localhost:2283/api"
API_KEY = os.environ.get("IMMICH_API_KEY")
DATE_AFTER = "2025-08-25T00:00:00.000Z"
DATE_BEFORE = "2025-08-29T00:00:00.000Z"
HEADERS = {
"x-api-key": API_KEY,
"Accept": "application/json",
"Content-Type": "application/json",
}
# First two lines are from postgres output, useless.
# Also the lines have single space as prefix, need to eliminate that too.
to_delete = [x.strip() for x in open('asset_ids_to_be_deleted.txt', 'r').read().splitlines()[2:]]
print('to_delete', to_delete[:10])
print(f"Found {len(to_delete)} assets to delete.")
if not to_delete:
exit("Nothing to delete. Check your date range.")
# === STEP 3: Delete in batches (API limit: 100 per call) ===
# I can only make 1 work, any larger number was not deleting fully.
#BATCH_SIZE = 100
BATCH_SIZE = 1
for i in range(0, len(to_delete), BATCH_SIZE):
batch = to_delete[i:i+BATCH_SIZE]
print(f"Deleting batch {i//BATCH_SIZE+1} ({len(batch)} items)...")
# print('batch', batch)
del_resp = requests.delete(
f"{IMMICH_URL}/assets",
headers=HEADERS,
json={"ids": batch, "force": True}
)
print('del_resp', del_resp)
if del_resp.status_code == 200:
print("Batch deleted successfully.")
else:
print("Error:", del_resp.status_code, del_resp.text)
print("✅ Deletion complete!")

Tricky part is setting the batch size to 1; I tried with multiple numbers, nothing worked even though api response is 200, the assets are not deleted.

SELECT count(*) FROM assets WHERE "ownerId" = '$OWNER_ID' AND "createdAt" >= '2025-08-26';