immich-go icon indicating copy to clipboard operation
immich-go copied to clipboard

Tags not attached to images

Open mammuth opened this issue 8 months ago • 23 comments

I see tags being created in Immich, however, the uploaded images don't have the tag attached.

Seems to affect --tag "test", --takeout-tag, --people-tag, as well as --session-tag.

./immich-go upload from-google-photos --server=http://immich.fritz.box:2283 --api-key=znamWTd4H6ijpqxbdguSJ6frAvcvjxa6UA1MObE1PhI --skip-verify-ssl --log-file --takeout-tag --people-tag --session-tag --tag "upload-run-3" --from-album-name "Shared test" /Users/max/Documents/google-photos-takeout-2/takeout-*.zip

The logs read like it successfully attached the one image that is part of the "Shared test" album that I was uploading 🤔

│2025-06-30 12:25:50 INF created tag tag=upload-run-3                                                                                                                                                                         │
│2025-06-30 12:25:50 INF updated tag tag=upload-run-3 assets=1                                                                                                                                                                │
│2025-06-30 12:25:50 INF created tag tag=takeout-20250629T224043Z                                                                                                                                                             │
│2025-06-30 12:25:50 INF updated tag tag=takeout-20250629T224043Z assets=1                                                                                                                                                    │
│2025-06-30 12:25:50 INF created tag tag={immich-go}/2025-06-30 12:25:43                                                                                                                                                      │
│2025-06-30 12:25:50 INF updated tag tag={immich-go}/2025-06-30 12:25:43 assets=1

Am I doing something wrong or did the tagging functionality maybe break recently? 🤔

In the Immich UI, the image detail view doesn't show the tag, the tag detail view doesn't show the image and searching for tags also doesn't return the image.

Image

immich-go: 0.27.0 immich: 1.135.3

PS: I'm trying to migrate from google photos to immich and I think it'd literally be impossible without the help of immich-go. Thanks for building it! ❤

mammuth avatar Jun 30 '25 10:06 mammuth

Am I doing something wrong or did the tagging functionality maybe break recently? 🤔

Um... can you run an import with --log-level=DEBUG and --api-trace set... Then share the logs on my discord account @simulot ?

simulot avatar Jun 30 '25 13:06 simulot

Um... can you run an import with --log-level=DEBUG and --api-trace set... Then share the logs on my discord account @simulot ?

The important pieces seem to be the following:

Logs:

2025-06-30 16:12:00 INF uploaded file=takeout-20250629T224043Z-1-001:Takeout/Google Fotos/Shared test/IMG_20250618_194917.jpg
2025-06-30 16:12:01 DBG  file.FileName=takeout-20250629T224043Z-1-001:Takeout/Google Fotos/Shared test/IMG_20250618_194917.jpg file.FileDate=2025-06-29 22:46:20 file.Description= file.Title=IMG_20250618_194917.jpg file.FileSize=4529672 file.ID=c1608b19-15e6-4887-b65c-0e7c8fda9fcb file.CaptureDate=2025-06-18 19:49:17 file.Trashed=false file.Archived=false file.FromPartner=false file.Favorite=false file.Stars=0 file.Latitude=48.xxxxx file.Longitude=11.xxxxx
2025-06-30 16:12:01 INF added to an album file=takeout-20250629T224043Z-1-001:Takeout/Google Fotos/Shared test/IMG_20250618_194917.jpg album=Shared test
2025-06-30 16:12:01 INF Tagged file=takeout-20250629T224043Z-1-001:Takeout/Google Fotos/Shared test/IMG_20250618_194917.jpg tag={immich-go}/2025-06-30 16:11:52
2025-06-30 16:12:01 INF Tagged file=takeout-20250629T224043Z-1-001:Takeout/Google Fotos/Shared test/IMG_20250618_194917.jpg tag=takeout-20250629T224043Z
2025-06-30 16:12:01 DBG CacheReader: remove temporary file name=/Users/max/Library/Caches/immich-go/temp/immich-go_252115217
2025-06-30 16:12:01 INF created album album=Shared test assets=1
2025-06-30 16:12:01 INF created tag tag={immich-go}/2025-06-30 16:11:52
2025-06-30 16:12:01 INF updated tag tag={immich-go}/2025-06-30 16:11:52 assets=1
2025-06-30 16:12:01 INF created tag tag=takeout-20250629T224043Z
2025-06-30 16:12:01 INF updated tag tag=takeout-20250629T224043Z assets=1

API Traces (show success messages):

2025-06-30T16:12:01+02:00 QUERY 26 UpsertTags PUT http://immich.fritz.box:2283/api/tags
   Content-Type [application/json]
   Accept [application/json]
   X-Api-Key redacted
-- body start --
{"tags":["{immich-go}/2025-06-30 16:11:52"]}
-- body end --

2025-06-30T16:12:01+02:00 RESPONSE 26 UpsertTags PUT http://immich.fritz.box:2283/api/tags
  Header:
     Connection : keep-alive
     Content-Type : application/json; charset=utf-8
     Content-Length : 246
     Vary : Accept-Encoding
     Keep-Alive : timeout=5
     X-Powered-By : Express
     X-Immich-Cid : fw56ov9e
     Etag : "f6-2m/qD6l6DHIyBcenfUN7AsxNNbQ"
     Date : Mon, 30 Jun 2025 14:12:01 GMT
  Status: 200 OK
-- response body start --
[{"id":"56dfc814-ae2c-482e-93a6-4f472f4225e4","parentId":"80de92ad-6e0e-450c-901c-63035f0202b0","name":"2025-06-30 16:11:52","value":"{immich-go}/2025-06-30 16:11:52","createdAt":"2025-06-30T14:12:01.188Z","updatedAt":"2025-06-30T14:12:01.188Z"}]
-- response body end --

2025-06-30T16:12:01+02:00 QUERY 27 TagAssets PUT http://immich.fritz.box:2283/api/tags/56dfc814-ae2c-482e-93a6-4f472f4225e4/assets
   Accept [application/json]
   X-Api-Key redacted
   Content-Type [application/json]
-- body start --
{"ids":["c1608b19-15e6-4887-b65c-0e7c8fda9fcb"]}
-- body end --

2025-06-30T16:12:01+02:00 RESPONSE 27 TagAssets PUT http://immich.fritz.box:2283/api/tags/56dfc814-ae2c-482e-93a6-4f472f4225e4/assets
  Header:
     Keep-Alive : timeout=5
     X-Immich-Cid : 1g0uoqsh
     Etag : "3e-P1Q6kR0nM4Dl+dp0rHjBednjB4I"
     Vary : Accept-Encoding
     Date : Mon, 30 Jun 2025 14:12:01 GMT
     X-Powered-By : Express
     Content-Type : application/json; charset=utf-8
     Content-Length : 62
     Connection : keep-alive
  Status: 200 OK
-- response body start --
[{"id":"c1608b19-15e6-4887-b65c-0e7c8fda9fcb","success":true}]
-- response body end --

2025-06-30T16:12:01+02:00 QUERY 28 UpsertTags PUT http://immich.fritz.box:2283/api/tags
   Content-Type [application/json]
   Accept [application/json]
   X-Api-Key redacted
-- body start --
{"tags":["takeout-20250629T224043Z"]}
-- body end --

2025-06-30T16:12:01+02:00 RESPONSE 28 UpsertTags PUT http://immich.fritz.box:2283/api/tags
  Header:
     X-Powered-By : Express
     X-Immich-Cid : 1g0fbf9o
     Content-Type : application/json; charset=utf-8
     Content-Length : 194
     Etag : "c2-HTEKVL2PfOKtoVAq6rwLJ8+9sGg"
     Date : Mon, 30 Jun 2025 14:12:01 GMT
     Keep-Alive : timeout=5
     Vary : Accept-Encoding
     Connection : keep-alive
  Status: 200 OK
-- response body start --
[{"id":"ad495306-a400-46b1-b2ed-ce1397f69323","name":"takeout-20250629T224043Z","value":"takeout-20250629T224043Z","createdAt":"2025-06-30T14:12:01.230Z","updatedAt":"2025-06-30T14:12:01.230Z"}]
-- response body end --

2025-06-30T16:12:01+02:00 QUERY 29 TagAssets PUT http://immich.fritz.box:2283/api/tags/ad495306-a400-46b1-b2ed-ce1397f69323/assets
   Content-Type [application/json]
   Accept [application/json]
   X-Api-Key redacted
-- body start --
{"ids":["c1608b19-15e6-4887-b65c-0e7c8fda9fcb"]}
-- body end --

2025-06-30T16:12:01+02:00 RESPONSE 29 TagAssets PUT http://immich.fritz.box:2283/api/tags/ad495306-a400-46b1-b2ed-ce1397f69323/assets
  Header:
     X-Powered-By : Express
     X-Immich-Cid : 3ecbas1o
     Content-Type : application/json; charset=utf-8
     Content-Length : 62
     Etag : "3e-P1Q6kR0nM4Dl+dp0rHjBednjB4I"
     Connection : keep-alive
     Keep-Alive : timeout=5
     Vary : Accept-Encoding
     Date : Mon, 30 Jun 2025 14:12:01 GMT
  Status: 200 OK
-- response body start --
[{"id":"c1608b19-15e6-4887-b65c-0e7c8fda9fcb","success":true}]
-- response body end --

Let me know if this doesn't cover everything you were looking for.

Looks pretty much like the upstream issue (https://github.com/immich-app/immich/issues/16747) you already spotted a few months ago, no? Based on the referenced PRs, immich itself apparently workarounds this issue by using bulkTagAssets instead?

mammuth avatar Jun 30 '25 14:06 mammuth

The logs show that the API call to create the tag and assign the tag have done their job...

I have tested the feature with latest immich and immich-go:

Image

Question: are you using the Storage Template feature?

simulot avatar Jun 30 '25 16:06 simulot

Yep, I'm using a storage template.

mammuth avatar Jun 30 '25 16:06 mammuth

Can you see if the job STORAGE TEMPLATE MIGRATION is paused by Immich-go

2025-06-30 18:41:08 INF Immich Job command sent pause=storageTemplateMigration

2025-06-30T18:41:08+02:00 QUERY 6 SendJobCommand PUT http://localhost:2283/api/jobs/storageTemplateMigration
   Content-Type [application/json]
   X-Api-Key redacted
-- body start --
{"command":"pause","force":true}
-- body end --

2025-06-30T18:41:08+02:00 RESPONSE 6 SendJobCommand PUT http://localhost:2283/api/jobs/storageTemplateMigration
  Header:
     Content-Length : 135
     Etag : "87-m5BapuJRp90X/X1K8Nf2NcLEoZQ"
     Vary : Accept-Encoding
     Date : Mon, 30 Jun 2025 16:41:08 GMT
     Connection : keep-alive
     X-Immich-Cid : cvfw0u0s
     Content-Type : application/json; charset=utf-8
     Keep-Alive : timeout=5
     X-Powered-By : Express
  Status: 200 OK
-- response body start --
{"jobCounts":{"active":0,"completed":0,"failed":0,"delayed":0,"waiting":0,"paused":0},"queueStatus":{"isActive":false,"isPaused":true}}
-- response body end --

simulot avatar Jun 30 '25 16:06 simulot

You can set the server's log level to debug as well.

simulot avatar Jun 30 '25 16:06 simulot

Just tested it with storage templates disabled, then it works.

With storage templates enabled, the traces show that the job is paused and once done, resumed:

2025-06-30T18:54:29+02:00 QUERY 16 SendJobCommand PUT http://immich.fritz.box:2283/api/jobs/storageTemplateMigration
   Content-Type [application/json]
   X-Api-Key redacted
-- body start --
{"command":"pause","force":true}
-- body end --

2025-06-30T18:54:29+02:00 RESPONSE 16 SendJobCommand PUT http://immich.fritz.box:2283/api/jobs/storageTemplateMigration
  Header:
     X-Powered-By : Express
     Content-Length : 135
     Etag : "87-m5BapuJRp90X/X1K8Nf2NcLEoZQ"
     Keep-Alive : timeout=5
     X-Immich-Cid : xoeqmzhq
     Content-Type : application/json; charset=utf-8
     Vary : Accept-Encoding
     Date : Mon, 30 Jun 2025 16:54:29 GMT
     Connection : keep-alive
  Status: 200 OK
-- response body start --
{"jobCounts":{"active":0,"completed":0,"failed":0,"delayed":0,"waiting":0,"paused":0},"queueStatus":{"isActive":false,"isPaused":true}}
-- response body end --


...


2025-06-30T18:54:37+02:00 QUERY 42 SendJobCommand PUT http://immich.fritz.box:2283/api/jobs/storageTemplateMigration
   Content-Type [application/json]
   X-Api-Key redacted
-- body start --
{"command":"resume","force":true}
-- body end --

2025-06-30T18:54:37+02:00 RESPONSE 42 SendJobCommand PUT http://immich.fritz.box:2283/api/jobs/storageTemplateMigration
  Header:
     Vary : Accept-Encoding
     X-Powered-By : Express
     Etag : "88-42u5DlZiqvmADg8mjxL2JXvkN5I"
     Date : Mon, 30 Jun 2025 16:54:37 GMT
     Connection : keep-alive
     Keep-Alive : timeout=5
     X-Immich-Cid : fad46fe9
     Content-Type : application/json; charset=utf-8
     Content-Length : 136
  Status: 200 OK
-- response body start --
{"jobCounts":{"active":0,"completed":0,"failed":0,"delayed":0,"waiting":0,"paused":0},"queueStatus":{"isActive":false,"isPaused":false}}
-- response body end --

Aren't you able to reproduce it, too, when using storage templates?

mammuth avatar Jun 30 '25 17:06 mammuth

Aren't you able to reproduce it, too, when using storage templates?

It will likely fail too. The job pause trick used to work with previous major version of immich

I don't see anything I can do to make immich work. I'd suggest filing an issue with the immich project

simulot avatar Jun 30 '25 17:06 simulot

I am facing this issue with the tags as well. Meanwhile, I suppose to workaround the issue, I should turn off the storage template engine, perform the upload, then turn the storage template back on and run the Storage Template Migration job?

chriseow avatar Jul 15 '25 09:07 chriseow

I'm running into this issue currently even without having Storage Templates on. Assuming my understanding of the underlying issue is correct, would it be possible to add an option to immich-go that creates a standalone script containing all of the API commands that set the tags (maybe with something like --create-tagging-script)? That way we could manually run that after all of the immich jobs are completed. Not sure how feasible or difficult that would be. I'm just spitballing.

WhiteWolfKevin avatar Jul 27 '25 13:07 WhiteWolfKevin

Facing this issue as well. @simulot isn't there something we can do even as a workaround while they fix it upstream? Like delaying the update somehow?

I don't fully understand what causes the issue. But I'm stuck not able to upload, and I'm happy to do some manual lifting if that means not losing the proper tags.

If the issue is updating a photo right after uploading, I find it strange that the album assignation works, but the tags don't.

Chaoscontrol avatar Jul 28 '25 20:07 Chaoscontrol

For those of you facing this issue and looking for a temporary workaround.

I have built a patched version with a new option to skip tagging and improve logs to allow for manual tagging via easy API calls. You can find more info a download it here.

Disclaimer: I am no dev. I have no clue of what I'm doing. This was done mostly using AI. This is the first time ever I create a release in Github, and even my own repo. The code might not be perfect. BUT it works.

Chaoscontrol avatar Jul 30 '25 17:07 Chaoscontrol

Is it possible this issue is not only specific to tags?

Doing a few test updates repeatedly and I'm finding the same kind of behaviour for ALL metadata. Result is that photos upload fine, but a few seconds right after, when refreshing the server page, some photos metadata has been wiped. It's easily seen because some photos suddenly move position and show up with today's date instead.

It's inconsistent, sometimes they all keep the data (in my test upload of 4 photos), sometimes a few are retained and a few wiped. It's quite random, which made me instantly think back to this issue with the tags.

And really, since I "think" both cases work using the updateAsset API call, right after upload... this seems strongly related...

Again very surprised no-one has reported this before. Metadata is way more used than tags, and people even more fussy about it.

Chaoscontrol avatar Aug 03 '25 14:08 Chaoscontrol

Facing the same issue, tags are missing, tags are created, but not attached to images

Can any one share more details on, how storage template and tags are related? And what would be possible fix on upstream side? Thn i can file an issue there, and may be see if i can create a PR (I am a dev, but new to immich)

@simulot

I also see, some of following stack traces in server logs

[Nest] 6  - 08/12/2025, 7:00:05 AM    WARN [Microservices:Error: File not found - /usr/src/app/upload/upload/4f56e5e0-3ab2-42a4-8cad-209d3d0543ec/59/46/59468239-921b-4a2c-97f5-0a9493a7b16c.jpg.xmp

    at ReadTask._ExifToolTask_parser (/usr/src/app/server/node_modules/exiftool-vendored/dist/ExifToolTask.js:76:71)

    at ReadTask.parser (/usr/src/app/server/node_modules/exiftool-vendored/dist/ExifToolTask.js:47:167)

    at ReadTask._Task_resolve (/usr/src/app/server/node_modules/batch-cluster/dist/Task.js:146:40)

    at runNextTicks (node:internal/process/task_queues:65:5)

    at listOnTimeout (node:internal/timers:549:9)

    at process.processTimers (node:internal/timers:523:7)] Error reading exif data (/usr/src/app/upload/upload/4f56e5e0-3ab2-42a4-8cad-209d3d0543ec/59/46/59468239-921b-4a2c-97f5-0a9493a7b16c.jpg.xmp): Error: File not found - /usr/src/app/upload/upload/4f56e5e0-3ab2-42a4-8cad-209d3d0543ec/59/46/59468239-921b-4a2c-97f5-0a9493a7b16c.jpg.xmp

snimavat avatar Aug 12 '25 07:08 snimavat

Is it possible this issue is not only specific to tags?

Doing a few test updates repeatedly and I'm finding the same kind of behaviour for ALL metadata. Result is that photos upload fine, but a few seconds right after, when refreshing the server page, some photos metadata has been wiped. It's easily seen because some photos suddenly move position and show up with today's date instead.

It's inconsistent, sometimes they all keep the data (in my test upload of 4 photos), sometimes a few are retained and a few wiped. It's quite random, which made me instantly think back to this issue with the tags.

And really, since I "think" both cases work using the updateAsset API call, right after upload... this seems strongly related...

Again very surprised no-one has reported this before. Metadata is way more used than tags, and people even more fussy about it.

Yep, happened for me too Some of the photoes showed up under "today"

snimavat avatar Aug 12 '25 07:08 snimavat

An update for any one facing the issue with lost metadata and lost tags

Immich-go creates assets, and thn fires api updates for metadata and tags

There exist a race condition between metadata extraction job and sidecar write job And its possible for assets to loose metadata and tags

Solutions would be

  • Fix upstream, so metadata extraction job wouldnt overwrite the metadata updated through api
  • Or update immich-go to send the metadata as part of POST when creating new asset (POST /assets/ supports passing sidecarFile as part of multipart form data )

More details can be found at

  • https://github.com/immich-app/immich/pull/16901
  • https://discord.com/channels/979116623879368755/1399462843581071495
  • and immich-go channel on immich discord

snimavat avatar Aug 12 '25 11:08 snimavat

I've been working on a draft PR that implements a new flag --update-metadata-only, which I used to reprocess all the images from a takeout after the initial import had been affected by the reported issue.

It's clearly a workaround for the upstream bug in Immich, but if anyone is interested, you can find it here

https://github.com/piffio/immich-go/tree/bug/990-tags-not-attached-to-images

If @simulot is interested in adding this functionality, I can turn it into a proper PR

piffio avatar Sep 21 '25 20:09 piffio

@piffio , yes I do!

simulot avatar Sep 22 '25 10:09 simulot

For people who do not see images in albums/tags after uploading: please check if they are being matched to trashed images on the server. In particular, you might be affected by the issue described here where background tasks were paused and never unpaused by immich-go, e.g. because you quit the program prematurely once.

I applied a patch similar to @piffio's above , then re-uploaded images. What happened was: immich-go matched them to trashed images on the server (by checksum), so did not upload the image data, but did add tags/albums (because of my patch). However, I did not see them in the tags/albums pages in the UI, because they were still marked as trashed and trashed images are always hidden (which makes sense I guess).

The solution was to unpause the background tasks that clear out the trash backlog, and wait for those jobs to finish. After re-uploading again (using the patched immich-go), I now see my images in the expected tags/albums.

It does raise the question: shouldn't immich-go "un-trash" an image when it matches it during upload? After all, if a user re-uploads it, I think they expect it to be restored.

basploeger avatar Sep 24 '25 19:09 basploeger

The latest release fixes the job resuming.

Why immich-go should untrash images? For example, I have lot of junk photos in my takeout. I patiently sort them out, and I would be upset to see them again.

simulot avatar Sep 25 '25 06:09 simulot

Experiencing the same problem on latest stable (unable to run latest dev due to #1121).

Without digging it further, it does seem to be a race condition of some kind. immich-go's log order indicates it tries to tag before it creates tags, which is odd (and not sure that's what it's doing):

{"time":"2025-10-12T16:02:50.047583-07:00","level":"INFO","msg":"scanned video file","file":"a6700:private/M4ROOT/CLIP/C0092.MP4"}
{"time":"2025-10-12T16:02:50.047642-07:00","level":"INFO","msg":"scanned sidecar file","file":"a6700:private/M4ROOT/CLIP/C0092.xmp"}
{"time":"2025-10-12T16:02:50.047662-07:00","level":"WARN","msg":"useless file","file":"a6700:C0092M01.XML"}
{"time":"2025-10-12T16:02:50.064418-07:00","level":"INFO","msg":"Assets on the server: 1"}
{"time":"2025-10-12T16:02:55.743741-07:00","level":"INFO","msg":"uploaded","file":"a6700:private/M4ROOT/CLIP/C0092.MP4"}
{"time":"2025-10-12T16:02:55.743809-07:00","level":"DEBUG","msg":"","file":{"FileName":"a6700:private/M4ROOT/CLIP/C0092.MP4","FileDate":"2025-09-07T20:24:34-07:00","Description":"","Title":"C0092.MP4","FileSize":201607754,"ID":"e41af9da-862b-4610-917e-e73ac2b549df","CaptureDate":"2025-08-28T23:30:53-07:00","Trashed":false,"Archived":false,"FromPartner":false,"Favorite":false,"Stars":0,"Latitude":"0.xxxxx","Longitude":"0.xxxxx"}}
{"time":"2025-10-12T16:02:55.743881-07:00","level":"INFO","msg":"Tagged","file":"a6700:private/M4ROOT/CLIP/C0092.MP4","tag":"volume/a6700"}
{"time":"2025-10-12T16:02:55.743934-07:00","level":"INFO","msg":"Tagged","file":"a6700:private/M4ROOT/CLIP/C0092.MP4","tag":"{immich-go}/2025-10-12 16:02:50"}
{"time":"2025-10-12T16:02:55.751696-07:00","level":"INFO","msg":"created tag","tag":"{immich-go}/2025-10-12 16:02:50"}
{"time":"2025-10-12T16:02:55.756207-07:00","level":"INFO","msg":"updated tag","tag":"{immich-go}/2025-10-12 16:02:50","assets":1}
{"time":"2025-10-12T16:02:55.762208-07:00","level":"INFO","msg":"created tag","tag":"volume/a6700"}
{"time":"2025-10-12T16:02:55.765913-07:00","level":"INFO","msg":"updated tag","tag":"volume/a6700","assets":1}

I've rerun the same command a few times after hard deleting the file from Immich and deleting the tags, sometimes it gets tagged with both tags, sometimes with one, and sometimes with none of the tags.

niieani avatar Oct 12 '25 23:10 niieani

I've created a PR with a feature that workarounds the Immich bug by writing tags into a temporary XMP sidecar before uploading, instead of using Immich tag APIs after the upload. This means tags are attached as part of the initial POST asset upload, in the sidecarData multipart, instead of as subsequent calls to Immich's API. If there's no sidecar, we create one (with just the tags), and if there is one, we losslessly modify its copy, by adding tags to it before sending it to Immich.

See #1131.

niieani avatar Oct 13 '25 16:10 niieani

I've been working on a draft PR that implements a new flag --update-metadata-only, which I used to reprocess all the images from a takeout after the initial import had been affected by the reported issue.

It's clearly a workaround for the upstream bug in Immich, but if anyone is interested, you can find it here

https://github.com/piffio/immich-go/tree/bug/990-tags-not-attached-to-images

If @simulot is interested in adding this functionality, I can turn it into a proper PR thank you! this fixed what I've had a headache from the past couple days lol

YellowRoseCx avatar Oct 21 '25 21:10 YellowRoseCx

trashed images on the server

Thanks for the idea to check trashed images! I cleared the trash, and now the empty tags stopped appearing after "Extract metadata" job.

Before this, I deleted empty tags with a script like this one - sorry, it's in .NET

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Api-Key", apiKey); 
client.DefaultRequestHeaders.Add("Accept", "application/json");

var tags = JArray.Parse(await client.GetStringAsync($"{serverUrl}/tags"));
foreach (JObject tag in tags)
{ 
    var buckets = JArray.Parse(await client.GetStringAsync($"{serverUrl}/timeline/buckets?tagId={tag["id"]}"));
    if (buckets == null || buckets.Count == 0) 
		await client.DeleteAsync($"{serverUrl}/tags/{tag["id"]}");
}

joeblack3108 avatar Dec 02 '25 09:12 joeblack3108