[bug] Attachment cleanup might remove a file that was already being used #1106

Closed
opened 2026-04-10 20:50:12 +00:00 by Yonle · 12 comments
Contributor

Your setup

From source

Extra details

Alpine Linux

Version

Based of 0b061090ac

PostgreSQL version

18

What were you trying to do?

[Delete & Redraft] a post

What did you expect to happen?

Able to remade a post that has media attachment to it

What actually happened?

If attachment cleanup is enabled, These medias became unavailable

Logs

No response

Severity

No response

Have you searched for this issue?

  • I have double-checked and have not found this issue mentioned anywhere.
### Your setup From source ### Extra details Alpine Linux ### Version Based of 0b061090ac ### PostgreSQL version 18 ### What were you trying to do? [Delete & Redraft] a post ### What did you expect to happen? Able to remade a post that has media attachment to it ### What actually happened? If attachment cleanup is enabled, These medias became unavailable ### Logs _No response_ ### Severity _No response_ ### Have you searched for this issue? - [X] I have double-checked and have not found this issue mentioned anywhere.
Owner

The backend does not know of redrafts, it’s a FE feature as you already noticed in the FE version of this issue (one ticket about the same problem is sufficient; we can still move it if it was opened in the wrong repo)

There is however a grace period before media cleanup is attempted, to allow republication. This can be configured via :instance, :cleanup_attachments_delay. If the 30min default is not sufficient, you can raise it.

Dupe of #775

The backend does not know of redrafts, it’s a FE feature as you already noticed in the FE version of this issue *(one ticket about the same problem is sufficient; we can still move it if it was opened in the wrong repo)* There is however a grace period before media cleanup is attempted, to allow republication. This can be configured via `:instance, :cleanup_attachments_delay`. If the 30min default is not sufficient, you can raise it. Dupe of https://akkoma.dev/AkkomaGang/akkoma/issues/775
Oneric 2026-04-10 21:08:57 +00:00
Author
Contributor

@Oneric wrote in #1106 (comment):

There is however a grace period before media cleanup is attempted, to allow republication. This can be configured via :instance, :cleanup_attachments_delay. If the 30min default is not sufficient, you can raise it.

Strangely, even after such period of time, The attachment gets cleaned up anyway even after it's being posted before the grade period.

@Oneric wrote in https://akkoma.dev/AkkomaGang/akkoma/issues/1106#issuecomment-16705: > There is however a grace period before media cleanup is attempted, to allow republication. This can be configured via `:instance, :cleanup_attachments_delay`. If the 30min default is not sufficient, you can raise it. Strangely, even after such period of time, The attachment gets cleaned up anyway even after it's being posted before the grade period.
Owner

hmm, did you change something about your upload base URL or changed uploaders?
I’d need to check how the current cleanup logic works again, but this might possibly lead to it thinking nothing else is using the file (since no other upload has the same URL)

Or maybe do you have filename query args enabled s.t. the same upload can have matching domain and path but different query strings? Not sure if that’s relevant or taken into account atm, but i believe all who were testing it when the grace period was introduced do not enable filename hints.

hmm, did you change something about your upload base URL or changed uploaders? I’d need to check how the current cleanup logic works again, but this might possibly lead to it thinking nothing else is using the file (since no other upload has the same URL) Or maybe do you have filename query args enabled s.t. the same upload can have matching domain and path but different query strings? Not sure if that’s relevant or taken into account atm, but i believe all who were testing it when the grace period was introduced do not enable filename hints.
Author
Contributor

@Oneric wrote in #1106 (comment):

hmm, did you change something about your upload base URL or changed uploaders?

I use S3 object storage and only that. The original logic is intact.

Or maybe do you have filename query args enabled

No. I have AnonymizedFilename enabled.

@Oneric wrote in https://akkoma.dev/AkkomaGang/akkoma/issues/1106#issuecomment-16716: > hmm, did you change something about your upload base URL or changed uploaders? I use S3 object storage and only that. The original logic is intact. > Or maybe do you have filename query args enabled No. I have AnonymizedFilename enabled.
Author
Contributor

For a side note, I:

not sure whenever it can give you some idea

For a side note, I: - locate the s3 to objstorage.domain.com - but the media base url is on https://media.example.com/media/ not sure whenever it can give you some idea
Owner

No. I have AnonymizedFilename enabled.

For reference, AnonymizedFilenames only replaces the filename but doesn’t control whether the query omitted from URLs. The latter is controlled by Pleroma.Upload, :link_name. Doesn’t seem like this is currently enabled on your instance either though (Whether or not this split makes sense is debatable, but it’s the config scheme we inherited.)

not sure whenever it can give you some idea

Could you query a post from the db, both before and after the redraft, so we can take a look at how it’s actually encoded?
Instructions how to fetch posts from the db via the ID you see in URLs and the API can be found in the README here: https://akkoma.dev/Oneric/shinyrod

Additionally, since this is about uploads, can you also query the objects table for the stand-alone attachment? You’ll find the "id" of the attachent in the post object’s data and can then query it with
SELECT * FROM objects WHERE data->>'id' = 'the id property found within the posts attachment array';.

Then wait and see whether the file and corresponding db objects actually remain preserved an hour later or both (r only one) got deleted.

Let me know if you need more detailed instructions

> No. I have AnonymizedFilename enabled. For reference, `AnonymizedFilenames` only _replaces_ the filename but doesn’t control whether the query omitted from URLs. The latter is controlled by `Pleroma.Upload, :link_name`. Doesn’t seem like this is currently enabled on your instance either though *(Whether or not this split makes sense is debatable, but it’s the config scheme we inherited.)* > not sure whenever it can give you some idea Could you query a post from the db, both before and after the redraft, so we can take a look at how it’s actually encoded? Instructions how to fetch posts from the db via the ID you see in URLs and the API can be found in the README here: https://akkoma.dev/Oneric/shinyrod Additionally, since this is about uploads, can you also query the `objects` table for the stand-alone attachment? You’ll find the `"id"` of the attachent in the post object’s `data` and can then query it with `SELECT * FROM objects WHERE data->>'id' = 'the id property found within the posts attachment array';`. Then wait and see whether the file and corresponding db objects actually remain preserved an hour later or both (r only one) got deleted. Let me know if you need more detailed instructions
Yonle changed title from [bug] [Delete & Redraft] in Akkoma-FE won't retain media attachment when Attachment cleanup is enabled. to [bug] Attachment cleanup might remove a file that was already being used 2026-04-12 08:49:24 +00:00
Author
Contributor

sorry for the late response.

@Oneric wrote in #1106 (comment):

Could you query a post from the db, both before and after the redraft, so we can take a look at how it’s actually encoded?

i will see whenever i can did it.

sorry for the late response. @Oneric wrote in https://akkoma.dev/AkkomaGang/akkoma/issues/1106#issuecomment-16738: > Could you query a post from the db, both before and after the redraft, so we can take a look at how it’s actually encoded? i will see whenever i can did it.
Author
Contributor
db object before and after

FRESH:

id data inserted_at updated_at
726645 {"cc": ["https://fedinet.waltuh.cyou/users/yonle/followers"], "id": "https://fedinet.waltuh.cyou/objects/1c7625e5-7baa-421f-a80f-e4773345196d", "to": ["https://www.w3.org/ns/activitystreams#Public"], "tag": [], "type": "Note", "actor": "https://fedinet.waltuh.cyou/users/yonle", "emoji": {}, "source": {"content": "TEST: `SELECT * FROM objects WHERE data->>'id' = 'ID';`\r\n\r\nOBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE & AFTER REDRAFT\r\n\r\nSTAGE: [1/4]: FRESH", "mediaType": "text/x.misskeymarkdown"}, "content": "<p>TEST: <code class=\"inline\">SELECT * FROM objects WHERE data-&gt;&gt;&#39;id&#39; = &#39;ID&#39;;</code></p><p>OBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp; AFTER REDRAFT</p><p>STAGE: [1/4]: FRESH</p>", "context": "https://fedinet.waltuh.cyou/contexts/4fe227e2-c2e8-4446-924b-e50035741777", "htmlMfm": true, "summary": "", "generator": null, "published": "2026-05-04T07:55:15.468512Z", "sensitive": null, "attachment": [{"id": "https://fedinet.waltuh.cyou/objects/c0691d96-1cdd-429e-98ff-0f8c932293a3", "url": [{"href": "https://media.fedinet.waltuh.cyou/media/c06fd589bd44d8bc519f1a80c006f01f1ac190c62758c96a0529a6e26d5e41a8.png", "type": "Link", "width": 512, "height": 512, "mediaType": "image/png"}], "name": "", "type": "Document", "actor": "https://fedinet.waltuh.cyou/users/yonle", "blurhash": "eORV^GDzxU_3My%MaexuogM|t7axRjj]t7_3ayM{oJt7t6j@V[ayof", "mediaType": "image/png"}], "contentMap": {"en": "<p>TEST: <code class=\"inline\">SELECT * FROM objects WHERE data-&gt;&gt;&#39;id&#39; = &#39;ID&#39;;</code></p><p>OBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp; AFTER REDRAFT</p><p>STAGE: [1/4]: FRESH</p>"}} 2026-05-04 07:55:15 2026-05-04 07:55:15

(1 row)

REDRAFTED (NO CLEANUP ENABLED):

id data inserted_at updated_at
726664 {"cc": ["https://fedinet.waltuh.cyou/users/yonle/followers"], "id": "https://fedinet.waltuh.cyou/objects/636a7179-8fc2-4510-acd5-3a3f7b81111d", "to": ["https://www.w3.org/ns/activitystreams#Public"], "tag": [], "type": "Note", "actor": "https://fedinet.waltuh.cyou/users/yonle", "emoji": {}, "source": {"content": "TEST: `SELECT * FROM objects WHERE data->>'id' = 'ID';`\r\n\r\nOBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE & AFTER REDRAFT\r\n\r\nSTAGE: [2/4]: REDRAFTED (NO CLEANP ENABLED)", "mediaType": "text/x.misskeymarkdown"}, "content": "<p>TEST: <code class=\"inline\">SELECT * FROM objects WHERE data-&gt;&gt;&#39;id&#39; = &#39;ID&#39;;</code></p><p>OBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp; AFTER REDRAFT</p><p>STAGE: [2/4]: REDRAFTED (NO CLEANP ENABLED)</p>", "context": "https://fedinet.waltuh.cyou/contexts/260d16a7-2393-45f9-9e0a-5bbad7925bba", "htmlMfm": true, "summary": "", "generator": null, "published": "2026-05-04T08:01:42.242605Z", "sensitive": null, "attachment": [{"id": "https://fedinet.waltuh.cyou/objects/c0691d96-1cdd-429e-98ff-0f8c932293a3", "url": [{"href": "https://media.fedinet.waltuh.cyou/media/c06fd589bd44d8bc519f1a80c006f01f1ac190c62758c96a0529a6e26d5e41a8.png", "type": "Link", "width": 512, "height": 512, "mediaType": "image/png"}], "name": "", "type": "Document", "actor": "https://fedinet.waltuh.cyou/users/yonle", "blurhash": "eORV^GDzxU_3My%MaexuogM|t7axRjj]t7_3ayM{oJt7t6j@V[ayof", "mediaType": "image/png"}], "contentMap": {"en": "<p>TEST: <code class=\"inline\">SELECT * FROM objects WHERE data-&gt;&gt;&#39;id&#39; = &#39;ID&#39;;</code></p><p>OBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp; AFTER REDRAFT</p><p>STAGE: [2/4]: REDRAFTED (NO CLEANP ENABLED)</p>"}} 2026-05-04 08:01:42 2026-05-04 08:01:42

(1 row)

<details> <summary>db object before and after</summary> FRESH: <table border="1"> <tr> <th align="center">id</th> <th align="center">data</th> <th align="center">inserted_at</th> <th align="center">updated_at</th> </tr> <tr valign="top"> <td align="right">726645</td> <td align="left">{&quot;cc&quot;: [&quot;https://fedinet.waltuh.cyou/users/yonle/followers&quot;], &quot;id&quot;: &quot;https://fedinet.waltuh.cyou/objects/1c7625e5-7baa-421f-a80f-e4773345196d&quot;, &quot;to&quot;: [&quot;https://www.w3.org/ns/activitystreams#Public&quot;], &quot;tag&quot;: [], &quot;type&quot;: &quot;Note&quot;, &quot;actor&quot;: &quot;https://fedinet.waltuh.cyou/users/yonle&quot;, &quot;emoji&quot;: {}, &quot;source&quot;: {&quot;content&quot;: &quot;TEST: `SELECT * FROM objects WHERE data-&gt;&gt;'id' = 'ID';`\r\n\r\nOBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp; AFTER REDRAFT\r\n\r\nSTAGE: [1/4]: FRESH&quot;, &quot;mediaType&quot;: &quot;text/x.misskeymarkdown&quot;}, &quot;content&quot;: &quot;&lt;p&gt;TEST: &lt;code class=\&quot;inline\&quot;&gt;SELECT * FROM objects WHERE data-&amp;gt;&amp;gt;&amp;#39;id&amp;#39; = &amp;#39;ID&amp;#39;;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;OBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp;amp; AFTER REDRAFT&lt;/p&gt;&lt;p&gt;STAGE: [1/4]: FRESH&lt;/p&gt;&quot;, &quot;context&quot;: &quot;https://fedinet.waltuh.cyou/contexts/4fe227e2-c2e8-4446-924b-e50035741777&quot;, &quot;htmlMfm&quot;: true, &quot;summary&quot;: &quot;&quot;, &quot;generator&quot;: null, &quot;published&quot;: &quot;2026-05-04T07:55:15.468512Z&quot;, &quot;sensitive&quot;: null, &quot;attachment&quot;: [{&quot;id&quot;: &quot;https://fedinet.waltuh.cyou/objects/c0691d96-1cdd-429e-98ff-0f8c932293a3&quot;, &quot;url&quot;: [{&quot;href&quot;: &quot;https://media.fedinet.waltuh.cyou/media/c06fd589bd44d8bc519f1a80c006f01f1ac190c62758c96a0529a6e26d5e41a8.png&quot;, &quot;type&quot;: &quot;Link&quot;, &quot;width&quot;: 512, &quot;height&quot;: 512, &quot;mediaType&quot;: &quot;image/png&quot;}], &quot;name&quot;: &quot;&quot;, &quot;type&quot;: &quot;Document&quot;, &quot;actor&quot;: &quot;https://fedinet.waltuh.cyou/users/yonle&quot;, &quot;blurhash&quot;: &quot;eORV^GDzxU_3My%MaexuogM|t7axRjj]t7_3ayM{oJt7t6j@V[ayof&quot;, &quot;mediaType&quot;: &quot;image/png&quot;}], &quot;contentMap&quot;: {&quot;en&quot;: &quot;&lt;p&gt;TEST: &lt;code class=\&quot;inline\&quot;&gt;SELECT * FROM objects WHERE data-&amp;gt;&amp;gt;&amp;#39;id&amp;#39; = &amp;#39;ID&amp;#39;;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;OBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp;amp; AFTER REDRAFT&lt;/p&gt;&lt;p&gt;STAGE: [1/4]: FRESH&lt;/p&gt;&quot;}}</td> <td align="left">2026-05-04 07:55:15</td> <td align="left">2026-05-04 07:55:15</td> </tr> </table> <p>(1 row)<br /> </p> REDRAFTED (NO CLEANUP ENABLED): <table border="1"> <tr> <th align="center">id</th> <th align="center">data</th> <th align="center">inserted_at</th> <th align="center">updated_at</th> </tr> <tr valign="top"> <td align="right">726664</td> <td align="left">{&quot;cc&quot;: [&quot;https://fedinet.waltuh.cyou/users/yonle/followers&quot;], &quot;id&quot;: &quot;https://fedinet.waltuh.cyou/objects/636a7179-8fc2-4510-acd5-3a3f7b81111d&quot;, &quot;to&quot;: [&quot;https://www.w3.org/ns/activitystreams#Public&quot;], &quot;tag&quot;: [], &quot;type&quot;: &quot;Note&quot;, &quot;actor&quot;: &quot;https://fedinet.waltuh.cyou/users/yonle&quot;, &quot;emoji&quot;: {}, &quot;source&quot;: {&quot;content&quot;: &quot;TEST: `SELECT * FROM objects WHERE data-&gt;&gt;'id' = 'ID';`\r\n\r\nOBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp; AFTER REDRAFT\r\n\r\nSTAGE: [2/4]: REDRAFTED (NO CLEANP ENABLED)&quot;, &quot;mediaType&quot;: &quot;text/x.misskeymarkdown&quot;}, &quot;content&quot;: &quot;&lt;p&gt;TEST: &lt;code class=\&quot;inline\&quot;&gt;SELECT * FROM objects WHERE data-&amp;gt;&amp;gt;&amp;#39;id&amp;#39; = &amp;#39;ID&amp;#39;;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;OBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp;amp; AFTER REDRAFT&lt;/p&gt;&lt;p&gt;STAGE: [2/4]: REDRAFTED (NO CLEANP ENABLED)&lt;/p&gt;&quot;, &quot;context&quot;: &quot;https://fedinet.waltuh.cyou/contexts/260d16a7-2393-45f9-9e0a-5bbad7925bba&quot;, &quot;htmlMfm&quot;: true, &quot;summary&quot;: &quot;&quot;, &quot;generator&quot;: null, &quot;published&quot;: &quot;2026-05-04T08:01:42.242605Z&quot;, &quot;sensitive&quot;: null, &quot;attachment&quot;: [{&quot;id&quot;: &quot;https://fedinet.waltuh.cyou/objects/c0691d96-1cdd-429e-98ff-0f8c932293a3&quot;, &quot;url&quot;: [{&quot;href&quot;: &quot;https://media.fedinet.waltuh.cyou/media/c06fd589bd44d8bc519f1a80c006f01f1ac190c62758c96a0529a6e26d5e41a8.png&quot;, &quot;type&quot;: &quot;Link&quot;, &quot;width&quot;: 512, &quot;height&quot;: 512, &quot;mediaType&quot;: &quot;image/png&quot;}], &quot;name&quot;: &quot;&quot;, &quot;type&quot;: &quot;Document&quot;, &quot;actor&quot;: &quot;https://fedinet.waltuh.cyou/users/yonle&quot;, &quot;blurhash&quot;: &quot;eORV^GDzxU_3My%MaexuogM|t7axRjj]t7_3ayM{oJt7t6j@V[ayof&quot;, &quot;mediaType&quot;: &quot;image/png&quot;}], &quot;contentMap&quot;: {&quot;en&quot;: &quot;&lt;p&gt;TEST: &lt;code class=\&quot;inline\&quot;&gt;SELECT * FROM objects WHERE data-&amp;gt;&amp;gt;&amp;#39;id&amp;#39; = &amp;#39;ID&amp;#39;;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;OBJ: SEE A DIFFERENCE ON ATTACHMENT ID VIA DB ON BEFORE &amp;amp; AFTER REDRAFT&lt;/p&gt;&lt;p&gt;STAGE: [2/4]: REDRAFTED (NO CLEANP ENABLED)&lt;/p&gt;&quot;}}</td> <td align="left">2026-05-04 08:01:42</td> <td align="left">2026-05-04 08:01:42</td> </tr> </table> <p>(1 row)<br /> </p> </details>
Owner

2.5h passed since the redraft was posted and the attachement seems to be still around. So everything seems to be working now?

Ah, you wrote "no cleanup enabled". This won’t show the full picture. And you are missing the attachment object itself

Additionally, since this is about uploads, can you also query the objects table for the stand-alone attachment? You’ll find the "id" of the attachent in the post object’s data and can then query it with
SELECT * FROM objects WHERE data->>'id' = 'the id property found within the posts attachment array';.

2.5h passed since the redraft was posted and the attachement seems to be still around. So everything seems to be working now? Ah, you wrote "no cleanup enabled". This won’t show the full picture. And you are missing the attachment object itself > Additionally, since this is about uploads, can you also query the objects table for the stand-alone attachment? You’ll find the "id" of the attachent in the post object’s data and can then query it with SELECT * FROM objects WHERE data->>'id' = 'the id property found within the posts attachment array';.
Author
Contributor

@Oneric wrote in #1106 (comment):

2.5h passed since the redraft was posted and the attachement seems to be still around. So everything seems to be working now?

as mentioned above, the cleanup is disabled, so i had no plan on reenabling this yet. but the other day i will, i guess.

@Oneric wrote in https://akkoma.dev/AkkomaGang/akkoma/issues/1106#issuecomment-16969: > 2.5h passed since the redraft was posted and the attachement seems to be still around. So everything seems to be working now? as mentioned above, the cleanup is disabled, so i had no plan on reenabling this yet. but the other day i will, i guess.
Author
Contributor

I don't really see the attachment ID from the post's object alone:

  "attachment": [
    {
      "blurhash": "M35h[QshQ*cH.Sp1MwI8WZ%hDis:H=a$o#",
      "height": 768,
      "mediaType": "image/png",
      "name": "",
      "type": "Document",
      "url": "https://media.fedinet.waltuh.cyou/media/52a41ba4f671edc793cf75623382dddff74208f28631072ccc76b15810911c68.png",
      "width": 1366
    }
  ],

do you get the attachment ID by the upload? or does the ID is the url?

I don't really see the attachment ID from the post's object alone: ``` "attachment": [ { "blurhash": "M35h[QshQ*cH.Sp1MwI8WZ%hDis:H=a$o#", "height": 768, "mediaType": "image/png", "name": "", "type": "Document", "url": "https://media.fedinet.waltuh.cyou/media/52a41ba4f671edc793cf75623382dddff74208f28631072ccc76b15810911c68.png", "width": 1366 } ], ``` do you get the attachment ID by the upload? or does the ID is the url?
Owner

Inside the database, local attachment entries have an id property; in the entries you previously shared this id is https://fedinet.waltuh.cyou/objects/c0691d96-1cdd-429e-98ff-0f8c932293a3.

When presenting the object to the outside world this id property is stripped out. Remote attachments generally do not have an id property.

Inside the database, local `attachment` entries have an `id` property; in the entries you previously shared this id is `https://fedinet.waltuh.cyou/objects/c0691d96-1cdd-429e-98ff-0f8c932293a3`. When presenting the object to the outside world this id property is stripped out. Remote attachments generally do not have an `id` property.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
AkkomaGang/akkoma#1106
No description provided.