Incoming Follow activity gets undone by (re)handling Undo of a different Follow #1120

Open
opened 2026-05-06 07:56:19 +00:00 by a · 17 comments
Contributor

Your setup

From source

Extra details

Arch Linux

Version

3.18.0-248-g479c7288-develop

PostgreSQL version

18.3

What were you trying to do?

accept a follow request

What did you expect to happen?

it gets accepted without fail

What actually happened?

it fails

Logs

May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.201 request_id=GKzmr7MF7loAqbIAdWUh [debug] POST /api/v1/follow_requests/AQFT4gmoLbbW6TrjMG/authorize
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.203 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] POST /api/v1/pleroma/notifications/read
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.203 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] Processing with Pleroma.Web.PleromaAPI.NotificationController.mark_as_read/2
May 06 06:39:55 trwnh.com mix[1145700]:   Parameters: %{"id" => "7103"}
May 06 06:39:55 trwnh.com mix[1145700]:   Pipelines: [:authenticated_api]
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.203 request_id=GKzmr7MF7loAqbIAdWUh [debug] Processing with Pleroma.Web.MastodonAPI.FollowRequestController.authorize/2
May 06 06:39:55 trwnh.com mix[1145700]:   Parameters: %{"id" => "AQFT4gmoLbbW6TrjMG"}
May 06 06:39:55 trwnh.com mix[1145700]:   Pipelines: [:authenticated_api]
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.204 request_id=GKzmr7MF7loAqbIAdWUh [debug] QUERY OK source="oauth_tokens" db=0.3ms idle=914.0ms
May 06 06:39:55 trwnh.com mix[1145700]: SELECT o0."id", o0."token", o0."refresh_token", o0."scopes", o0."valid_until", o0."user_id", o0."app_id", o0."inserted_at", o0."updated_at" FROM "oauth_tokens" AS o0 WHERE (o0."token" = $1) ["GJP0XC7vF1-gf0DgPXly7SH3yudvcCMFpz--tyaoKFo"]
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.205 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] QUERY OK source="oauth_tokens" db=1.3ms idle=914.7ms
May 06 06:39:55 trwnh.com mix[1145700]: SELECT o0."id", o0."token", o0."refresh_token", o0."scopes", o0."valid_until", o0."user_id", o0."app_id", o0."inserted_at", o0."updated_at" FROM "oauth_tokens" AS o0 WHERE (o0."token" = $1) ["GJP0XC7vF1-gf0DgPXly7SH3yudvcCMFpz--tyaoKFo"]
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.205 [debug] QUERY OK source="users" db=0.8ms idle=914.4ms
May 06 06:39:55 trwnh.com mix[1145700]: SELECT u0."id", u0."bio", u0."raw_bio", u0."email", u0."name", u0."nickname", u0."password_hash", u0."ap_id", u0."avatar", u0."local", u0."follower_address", u0."following_address", u0."featured_address", u0."tags", u0."last_refreshed_at", u0."last_digest_emailed_at", u0."banner", u0."background", u0."note_count", u0."follower_count", u0."following_count", u0."is_locked", u0."is_confirmed", u0."password_reset_pending", u0."is_approved", u0."registration_reason", u0."confirmation_token", u0."default_scope", u0."domain_blocks", u0."is_active", u0."no_rich_text", u0."is_moderator", u0."is_admin", u0."show_role", u0."mastofe_settings", u0."uri", u0."hide_followers_count", u0."hide_follows_count", u0."hide_followers", u0."hide_follows", u0."hide_favorites", u0."email_notifications", u0."mascot", u0."emoji", u0."pleroma_settings_store", u0."fields", u0."raw_fields", u0."is_discoverable", u0."invisible", u0."allow_following_move", u0."skip_thread_containment", u0."actor_type", u0."also_known_as", u0."inbox", u0."shared_inbox", u0."last_active_at", u0."disclose_client", u0."pinned_objects", u0."is_suggested", u0."last_status_at", u0."language", u0."status_ttl_days", u0."permit_followback", u0."accepts_direct_messages_from", u0."notification_settings", u0."multi_factor_authentication_settings", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) ["AQFT4gmoLbbW6TrjMG"]
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.207 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] QUERY OK source="notifications" db=1.4ms idle=914.4ms
May 06 06:39:55 trwnh.com mix[1145700]: SELECT n0."id", n0."seen", n0."type", n0."user_id", n0."activity_id", n0."inserted_at", n0."updated_at", a1."id", a1."data", a1."local", a1."actor", a1."recipients", a1."inserted_at", a1."updated_at" FROM "notifications" AS n0 INNER JOIN "activities" AS a1 ON a1."id" = n0."activity_id" WHERE (n0."id" = $1) [7103]
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.207 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] Sent 400 in 3ms
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.207 request_id=GKzmr7MF7loAqbIAdWUh [debug] QUERY OK source="activities" db=1.0ms idle=913.7ms
May 06 06:39:55 trwnh.com mix[1145700]: SELECT a0."id", a0."data", a0."local", a0."actor", a0."recipients", a0."inserted_at", a0."updated_at" FROM "activities" AS a0 WHERE ((a0."data")->>'type' = $1) AND (a0."actor" = $2) AND (coalesce((a0."data")->'object'->>'id', (a0."data")->>'object') = $3) ORDER BY a0."id" desc nulls last LIMIT 1 ["Follow", "https://xyzzy.link/users/wizard", "https://akkoma.trwnh.com/users/a"]
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.232 request_id=GKzmr7MF7loAqbIAdWUh [error] Internal server error: %FunctionClauseError{module: Pleroma.Web.MastodonAPI.FollowRequestController, function: :errors, arity: 2, kind: nil, args: nil, clauses: nil}
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.232 request_id=GKzmr7MF7loAqbIAdWUh [debug] Sent 500 in 30ms
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.232 request_id=GKzmr7MF7loAqbIAdWUh [debug] Converted error :function_clause to 500 response
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.240 request_id=GKzmr7Vak91M8TcAe3SC [debug] GET /images/city.jpg
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.241 request_id=GKzmr7Vak91M8TcAe3SC [debug] Processing with Pleroma.Web.Fallback.RedirectController.redirector_with_preload/2
May 06 06:39:55 trwnh.com mix[1145700]:   Parameters: %{"path" => ["images", "city.jpg"]}
May 06 06:39:55 trwnh.com mix[1145700]:   Pipelines: []
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.243 request_id=GKzmr7Vak91M8TcAe3SC [debug] QUERY OK source="users" db=0.5ms idle=836.2ms
May 06 06:39:55 trwnh.com mix[1145700]: SELECT count(*) FROM "users" AS u0 WHERE (u0."last_active_at" >= $1) AND (u0."local" = TRUE) [~N[2026-04-06 06:39:55]]
May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.233 [error] #PID<0.146343585.0> running Pleroma.Web.Endpoint (connection #PID<0.146342971.0>, stream id 1) terminated
May 06 06:39:55 trwnh.com mix[1145700]: Server: akkoma.trwnh.com:80 (http)
May 06 06:39:55 trwnh.com mix[1145700]: Request: POST /api/v1/follow_requests/AQFT4gmoLbbW6TrjMG/authorize
May 06 06:39:55 trwnh.com mix[1145700]: ** (exit) an exception was raised:
May 06 06:39:55 trwnh.com mix[1145700]:     ** (FunctionClauseError) no function clause matching in Pleroma.Web.MastodonAPI.FollowRequestController.errors/2
May 06 06:39:55 trwnh.com mix[1145700]:         (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:66: Pleroma.Web.MastodonAPI.FollowRequestController.errors(%Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{user: #Pleroma.User<__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: "AQDDR47on7cS3eqxZQ", bio: "a-kkoma aspect. || this instance uses an MRF i wrote to drop public addressing from all posts.", raw_bio: "a-kkoma aspect. || this instance uses an MRF i wrote to drop public addressing from all posts.", name: "a", nickname: "a", password_confirmation: nil, ap_id: "https://akkoma.trwnh.com/users/a", avatar: %{"id" => "https://akkoma.trwnh.com/objects/f608354e-b810-4be2-9bd8-61745f3baaeb", "mediaType" => "image/png", "name" => "", "type" => "Image", "url" => [%{"href" => "https://media.akkoma.trwnh.com/media/696fef76e90ae93211d1ad4618be58565f6a5c95343de5da8cc80864b5a9383c.png", "mediaType" => "image/png", "type" => "Link"}]}, local: true, follower_address: "https://akkoma.trwnh.com/users/a/followers", following_address: "https://akkoma.trwnh.com/users/a/following", featured_address: "https://akkoma.trwnh.com/users/a/collections/featured", search_rank: nil, search_type: nil, tags: [], last_refreshed_at: nil, last_digest_emailed_at: ~N[2022-12-02 23:59:13], banner: %{"id" => "https://akkoma.trwnh.com/objects/6d03bbf0-39d8-42bb-a1a4-1e4017222ca2", "mediaType" => "application/octet-stream", "name" => "", "type" => "Image", "url" => [%{"href" => "https://akkoma.trwnh.com/media/337f730f-96ee-4016-8ad8-08e3736f6393/cancel-you-thanks-for-being-patient-team-member", "mediaType" => "application/octet-stream", "type" => "Link"}]}, background: %{}, note_count: 0, follower_count: 116, following_count: 159, is_locked: true, is_confirmed: true, password_reset_pending: false, is_approved: true, registration_reason: nil, confirmation_token: nil, default_scope: "private", domain_blocks: [], is_active: true, no_rich_text: false, is_moderator: false, is_admin: true, show_role: false, mastofe_settings: %{"columns" => [%{"id" => "COMPOSE", "params" => %{}, "uuid" => "fa71d107-cc5a-4d1e-9e5c-10bb2f47599b"}, %{"id" => "HOME", "params" => %{}, "uuid" => "c59bc1d4-e62f-46ae-9222-3c7d5104121a"}, %{"id" => "NOTIFICATIONS", "params" => %{}, "uuid" => "e4644336-8677-4327-94f4-ec588e977900"}], "direct" => %{"regex" => %{"body" => ""}}, "domain" => %{"regex" => %{"body" => ""}}, "group" => %{"regex" => %{"body" => ""}}, "home" => %{"regex" => %{"body" => ""}, "shows" => %{"direct" => false, "limited" => false, "private" => false, "reblog" => true, "reply" => true}}, "introductionVersion" => 20181216044202, "limited" => %{"regex" => %{"body" => ""}, "shows" => %{"direct" => true, "limited" => true, "private" => true, "reblog" => true, ...}}, ...}, ...>, token: %Pleroma.Web.OAuth.Token{__meta__: #Ecto.Schema.Metadata<:loaded, "oauth_tokens">, id: 2034, token: "GJP0XC7vF1-gf0DgPXly7SH3yudvcCMFpz--tyaoKFo", refresh_token: "L3mKTxSvJGFVgtnPQyO_ik489Po7iIYS2W61bFZj5-c", scopes: ["read", "write", "follow", "push", "admin"], valid_until: ~N[2125-08-19 17:41:28.571539], user_id: "AQDDR47on7cS3eqxZQ", user: #Ecto.Association.NotLoaded<association :user is not loaded>, app_id: 311, app: #Ecto.Association.NotLoaded<association :app is not loaded>, inserted_at: ~N[2025-09-12 17:41:28], updated_at: ~N[2025-09-12 17:41:28]}, locale: "en", follower: #Pleroma.User<__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: "AQFT4gmoLbbW6TrjMG", bio: "A wizard in a world of witches.<br/><br/><a class=\"hashtag\" data-tag=\"nobot\" href=\"https://xyzzy.link/tag/nobot\" rel=\"tag ugc\">#nobot</a> unless you&#39;re friendly :)", raw_bio: nil, name: "wlo", nickname: "wizard@xyzzy.link", password_confirmation: nil, ap_id: "https://xyzzy.link/users/wizard", avatar: %{"type" => "Image", "url" => [%{"href" => "https://s3.us-east-2.wasabisys.com/xyzzylink-media/f9326efcb72cad77fad112debf67d87d8c632244569f705d6abd49c541fd86f6.gif"}]}, local: false, follower_address: "https://xyzzy.link/users/wizard/followers", following_address: "https://xyzzy.link/users/wizard/following", featured_address: "https://xyzzy.link/users/wizard/collections/featured", search_rank: nil, search_type: nil, tags: [], last_refreshed_at: ~N[2026-05-05 22:42:34.386652], last_digest_emailed_at: ~N[2022-12-04 02:03:52], banner: %{"type" => "Image", "url" => [%{"href" => "https://s3.us-east-2.wasabisys.com/xyzzylink-media/75f2fec27bd9815c2b5bd16c5db84a97d319998fdf5af4847925e19e4717c983.jpg"}]}, background: nil, note_count: 5366, follower_count: 987, following_count: 527, is_locked: false, is_confirmed: true, password_reset_pending: false, is_approved: true, registration_reason: nil, confirmation_token: nil, default_scope: "public", domain_blocks: [], is_active: true, no_rich_text: false, is_moderator: false, is_admin: false, show_role: true, mastofe_settings: nil, uri: "https://xyzzy.link/users/wizard", hide_followers_count: false, hide_follows_count: false, hide_followers: true, hide_follows: false, hide_favorites: true, email_notifications: %{"digest" => false}, mascot: nil, emoji: %{}, pleroma_settings_store: %{}, fields: [%{"name" => "AFPS", "value" => "Xonotic; CPMA; UT"}, %{"name" => "Datacenter", "value" => "Raver"}, %{"name" => "Pronoun", "value" => "she"}, %{"name" => "Location", "value" => "Neo England"}, %{"name" => "Website", "value" => "<a href=\"https://willow.phantoma.online\" rel=\"ugc\">https://willow.phantoma.online</a>"}], raw_fields: [], is_discoverable: true, invisible: false, ...>, csp_nonce: "9nbZDGpKafCvCR4", locales: ["en"]}, body_params: %{}, cookies: %{"__Host-pleroma_key" => "SFMyNTY.g3QAAAACbQAAAAtvYXV0aF90b2tlbm0AAAArR0pQMFhDN3ZGMS1nZjBEZ1BYbHk3U0gzeXVkdmNDTUZwei0tdHlhb0tGb20AAAAKb2F1dGhfdXNlcncDbmls.HQSC4WmbwVQtsadK_YhYCD0L_dZUCrGN1rbAvBadqE8", "userLanguage" => "en-US"}, halted: false, host: "akkoma.trwnh.com", method: "POST", owner: #PID<0.146343585.0>, params: %{id: "AQFT4gmoLbbW6TrjMG"}, path_info: ["api", "v1", "follow_requests", "AQFT4gmoLbbW6TrjMG", "authorize"], path_params: %{"id" => "AQFT4gmoLbbW6TrjMG"}, port: 80, private: %{:phoenix_view => %{"html" => Pleroma.Web.MastodonAPI.FollowRequestView, "json" => Pleroma.Web.MastodonAPI.FollowRequestView}, :open_api_spex => %{params: %{id: "AQFT4gmoLbbW6TrjMG"}, body_params: %{}, spec_module: Pleroma.Web.ApiSpec}, Pleroma.Web.Router => [], :phoenix_action => :authorize, :phoenix_controller => Pleroma.Web.MastodonAPI.FollowRequestController, :operation_id => "FollowRequestController.authorize", :phoenix_endpoint => Pleroma.Web.Endpoint, :called_plugs => [Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug, Pleroma.Web.Plugs.EnsureAuthenticatedPlug, Pleroma.Web.Plugs.OAuthScopesPlug], :phoenix_format => "json", :phoenix_router => Pleroma.Web.Router, :plug_session_info => :write, :plug_session_fetch => :done, :plug_session => %{"oauth_token" => "GJP0XC7vF1-gf0DgPXly7SH3yudvcCMFpz--tyaoKFo", "oauth_user" => nil}, :before_send => [#Function<0.76585640/1 in Plug.Session.before_send/2>, #Function<1.94339782/1 in Plug.Logger.call/2>], :phoenix_layout => %{"html" => {Pleroma.Web.LayoutView, :app}}}, query_params: %{}, query_string: "", remote_ip: {9728, 5888, 29952, 38560, 0, 0, 0, 2115}, req_cookies: %{"__Host-pleroma_key" => "SFMyNTY.g3QAAAACbQAAAAtvYXV0aF90b2tlbm0AAAArR0pQMFhDN3ZGMS1nZjBEZ1BYbHk3U0gzeXVkdmNDTUZwei0tdHlhb0tGb20AAAAKb2F1dGhfdXNlcncDbmls.HQSC4WmbwVQtsadK_YhYCD0L_dZUCrGN1rbAvBadqE8", "userLanguage" => "en-US"}, req_headers: [{"accept", "*/*"}, {"accept-encoding", "gzip, deflate, br, zstd"}, ...], ...}, nil)
May 06 06:39:55 trwnh.com mix[1145700]:         (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:5: Pleroma.Web.MastodonAPI.FollowRequestController.action/2
May 06 06:39:55 trwnh.com mix[1145700]:         (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:5: Pleroma.Web.MastodonAPI.FollowRequestController.phoenix_controller_pipeline/2
May 06 06:39:55 trwnh.com mix[1145700]:         (phoenix 1.8.1) lib/phoenix/router.ex:416: Phoenix.Router.__call__/5
May 06 06:39:55 trwnh.com mix[1145700]:         (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/endpoint.ex:5: Pleroma.Web.Endpoint.plug_builder_call/2
May 06 06:39:55 trwnh.com mix[1145700]:         (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/endpoint.ex:5: Pleroma.Web.Endpoint.call/2
May 06 06:39:55 trwnh.com mix[1145700]:         (plug_cowboy 2.7.4) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2
May 06 06:39:55 trwnh.com mix[1145700]:         (cowboy 2.14.2) /srv/akkoma/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2

Severity

I cannot use the software

Have you searched for this issue?

  • I have double-checked and have not found this issue mentioned anywhere.
### Your setup From source ### Extra details Arch Linux ### Version 3.18.0-248-g479c7288-develop ### PostgreSQL version 18.3 ### What were you trying to do? accept a follow request ### What did you expect to happen? it gets accepted without fail ### What actually happened? it fails ### Logs ```shell May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.201 request_id=GKzmr7MF7loAqbIAdWUh [debug] POST /api/v1/follow_requests/AQFT4gmoLbbW6TrjMG/authorize May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.203 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] POST /api/v1/pleroma/notifications/read May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.203 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] Processing with Pleroma.Web.PleromaAPI.NotificationController.mark_as_read/2 May 06 06:39:55 trwnh.com mix[1145700]: Parameters: %{"id" => "7103"} May 06 06:39:55 trwnh.com mix[1145700]: Pipelines: [:authenticated_api] May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.203 request_id=GKzmr7MF7loAqbIAdWUh [debug] Processing with Pleroma.Web.MastodonAPI.FollowRequestController.authorize/2 May 06 06:39:55 trwnh.com mix[1145700]: Parameters: %{"id" => "AQFT4gmoLbbW6TrjMG"} May 06 06:39:55 trwnh.com mix[1145700]: Pipelines: [:authenticated_api] May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.204 request_id=GKzmr7MF7loAqbIAdWUh [debug] QUERY OK source="oauth_tokens" db=0.3ms idle=914.0ms May 06 06:39:55 trwnh.com mix[1145700]: SELECT o0."id", o0."token", o0."refresh_token", o0."scopes", o0."valid_until", o0."user_id", o0."app_id", o0."inserted_at", o0."updated_at" FROM "oauth_tokens" AS o0 WHERE (o0."token" = $1) ["GJP0XC7vF1-gf0DgPXly7SH3yudvcCMFpz--tyaoKFo"] May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.205 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] QUERY OK source="oauth_tokens" db=1.3ms idle=914.7ms May 06 06:39:55 trwnh.com mix[1145700]: SELECT o0."id", o0."token", o0."refresh_token", o0."scopes", o0."valid_until", o0."user_id", o0."app_id", o0."inserted_at", o0."updated_at" FROM "oauth_tokens" AS o0 WHERE (o0."token" = $1) ["GJP0XC7vF1-gf0DgPXly7SH3yudvcCMFpz--tyaoKFo"] May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.205 [debug] QUERY OK source="users" db=0.8ms idle=914.4ms May 06 06:39:55 trwnh.com mix[1145700]: SELECT u0."id", u0."bio", u0."raw_bio", u0."email", u0."name", u0."nickname", u0."password_hash", u0."ap_id", u0."avatar", u0."local", u0."follower_address", u0."following_address", u0."featured_address", u0."tags", u0."last_refreshed_at", u0."last_digest_emailed_at", u0."banner", u0."background", u0."note_count", u0."follower_count", u0."following_count", u0."is_locked", u0."is_confirmed", u0."password_reset_pending", u0."is_approved", u0."registration_reason", u0."confirmation_token", u0."default_scope", u0."domain_blocks", u0."is_active", u0."no_rich_text", u0."is_moderator", u0."is_admin", u0."show_role", u0."mastofe_settings", u0."uri", u0."hide_followers_count", u0."hide_follows_count", u0."hide_followers", u0."hide_follows", u0."hide_favorites", u0."email_notifications", u0."mascot", u0."emoji", u0."pleroma_settings_store", u0."fields", u0."raw_fields", u0."is_discoverable", u0."invisible", u0."allow_following_move", u0."skip_thread_containment", u0."actor_type", u0."also_known_as", u0."inbox", u0."shared_inbox", u0."last_active_at", u0."disclose_client", u0."pinned_objects", u0."is_suggested", u0."last_status_at", u0."language", u0."status_ttl_days", u0."permit_followback", u0."accepts_direct_messages_from", u0."notification_settings", u0."multi_factor_authentication_settings", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) ["AQFT4gmoLbbW6TrjMG"] May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.207 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] QUERY OK source="notifications" db=1.4ms idle=914.4ms May 06 06:39:55 trwnh.com mix[1145700]: SELECT n0."id", n0."seen", n0."type", n0."user_id", n0."activity_id", n0."inserted_at", n0."updated_at", a1."id", a1."data", a1."local", a1."actor", a1."recipients", a1."inserted_at", a1."updated_at" FROM "notifications" AS n0 INNER JOIN "activities" AS a1 ON a1."id" = n0."activity_id" WHERE (n0."id" = $1) [7103] May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.207 request_id=GKzmr7MhEzyIdEgAe3Ry [debug] Sent 400 in 3ms May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.207 request_id=GKzmr7MF7loAqbIAdWUh [debug] QUERY OK source="activities" db=1.0ms idle=913.7ms May 06 06:39:55 trwnh.com mix[1145700]: SELECT a0."id", a0."data", a0."local", a0."actor", a0."recipients", a0."inserted_at", a0."updated_at" FROM "activities" AS a0 WHERE ((a0."data")->>'type' = $1) AND (a0."actor" = $2) AND (coalesce((a0."data")->'object'->>'id', (a0."data")->>'object') = $3) ORDER BY a0."id" desc nulls last LIMIT 1 ["Follow", "https://xyzzy.link/users/wizard", "https://akkoma.trwnh.com/users/a"] May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.232 request_id=GKzmr7MF7loAqbIAdWUh [error] Internal server error: %FunctionClauseError{module: Pleroma.Web.MastodonAPI.FollowRequestController, function: :errors, arity: 2, kind: nil, args: nil, clauses: nil} May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.232 request_id=GKzmr7MF7loAqbIAdWUh [debug] Sent 500 in 30ms May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.232 request_id=GKzmr7MF7loAqbIAdWUh [debug] Converted error :function_clause to 500 response May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.240 request_id=GKzmr7Vak91M8TcAe3SC [debug] GET /images/city.jpg May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.241 request_id=GKzmr7Vak91M8TcAe3SC [debug] Processing with Pleroma.Web.Fallback.RedirectController.redirector_with_preload/2 May 06 06:39:55 trwnh.com mix[1145700]: Parameters: %{"path" => ["images", "city.jpg"]} May 06 06:39:55 trwnh.com mix[1145700]: Pipelines: [] May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.243 request_id=GKzmr7Vak91M8TcAe3SC [debug] QUERY OK source="users" db=0.5ms idle=836.2ms May 06 06:39:55 trwnh.com mix[1145700]: SELECT count(*) FROM "users" AS u0 WHERE (u0."last_active_at" >= $1) AND (u0."local" = TRUE) [~N[2026-04-06 06:39:55]] May 06 06:39:55 trwnh.com mix[1145700]: 06:39:55.233 [error] #PID<0.146343585.0> running Pleroma.Web.Endpoint (connection #PID<0.146342971.0>, stream id 1) terminated May 06 06:39:55 trwnh.com mix[1145700]: Server: akkoma.trwnh.com:80 (http) May 06 06:39:55 trwnh.com mix[1145700]: Request: POST /api/v1/follow_requests/AQFT4gmoLbbW6TrjMG/authorize May 06 06:39:55 trwnh.com mix[1145700]: ** (exit) an exception was raised: May 06 06:39:55 trwnh.com mix[1145700]: ** (FunctionClauseError) no function clause matching in Pleroma.Web.MastodonAPI.FollowRequestController.errors/2 May 06 06:39:55 trwnh.com mix[1145700]: (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:66: Pleroma.Web.MastodonAPI.FollowRequestController.errors(%Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{user: #Pleroma.User<__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: "AQDDR47on7cS3eqxZQ", bio: "a-kkoma aspect. || this instance uses an MRF i wrote to drop public addressing from all posts.", raw_bio: "a-kkoma aspect. || this instance uses an MRF i wrote to drop public addressing from all posts.", name: "a", nickname: "a", password_confirmation: nil, ap_id: "https://akkoma.trwnh.com/users/a", avatar: %{"id" => "https://akkoma.trwnh.com/objects/f608354e-b810-4be2-9bd8-61745f3baaeb", "mediaType" => "image/png", "name" => "", "type" => "Image", "url" => [%{"href" => "https://media.akkoma.trwnh.com/media/696fef76e90ae93211d1ad4618be58565f6a5c95343de5da8cc80864b5a9383c.png", "mediaType" => "image/png", "type" => "Link"}]}, local: true, follower_address: "https://akkoma.trwnh.com/users/a/followers", following_address: "https://akkoma.trwnh.com/users/a/following", featured_address: "https://akkoma.trwnh.com/users/a/collections/featured", search_rank: nil, search_type: nil, tags: [], last_refreshed_at: nil, last_digest_emailed_at: ~N[2022-12-02 23:59:13], banner: %{"id" => "https://akkoma.trwnh.com/objects/6d03bbf0-39d8-42bb-a1a4-1e4017222ca2", "mediaType" => "application/octet-stream", "name" => "", "type" => "Image", "url" => [%{"href" => "https://akkoma.trwnh.com/media/337f730f-96ee-4016-8ad8-08e3736f6393/cancel-you-thanks-for-being-patient-team-member", "mediaType" => "application/octet-stream", "type" => "Link"}]}, background: %{}, note_count: 0, follower_count: 116, following_count: 159, is_locked: true, is_confirmed: true, password_reset_pending: false, is_approved: true, registration_reason: nil, confirmation_token: nil, default_scope: "private", domain_blocks: [], is_active: true, no_rich_text: false, is_moderator: false, is_admin: true, show_role: false, mastofe_settings: %{"columns" => [%{"id" => "COMPOSE", "params" => %{}, "uuid" => "fa71d107-cc5a-4d1e-9e5c-10bb2f47599b"}, %{"id" => "HOME", "params" => %{}, "uuid" => "c59bc1d4-e62f-46ae-9222-3c7d5104121a"}, %{"id" => "NOTIFICATIONS", "params" => %{}, "uuid" => "e4644336-8677-4327-94f4-ec588e977900"}], "direct" => %{"regex" => %{"body" => ""}}, "domain" => %{"regex" => %{"body" => ""}}, "group" => %{"regex" => %{"body" => ""}}, "home" => %{"regex" => %{"body" => ""}, "shows" => %{"direct" => false, "limited" => false, "private" => false, "reblog" => true, "reply" => true}}, "introductionVersion" => 20181216044202, "limited" => %{"regex" => %{"body" => ""}, "shows" => %{"direct" => true, "limited" => true, "private" => true, "reblog" => true, ...}}, ...}, ...>, token: %Pleroma.Web.OAuth.Token{__meta__: #Ecto.Schema.Metadata<:loaded, "oauth_tokens">, id: 2034, token: "GJP0XC7vF1-gf0DgPXly7SH3yudvcCMFpz--tyaoKFo", refresh_token: "L3mKTxSvJGFVgtnPQyO_ik489Po7iIYS2W61bFZj5-c", scopes: ["read", "write", "follow", "push", "admin"], valid_until: ~N[2125-08-19 17:41:28.571539], user_id: "AQDDR47on7cS3eqxZQ", user: #Ecto.Association.NotLoaded<association :user is not loaded>, app_id: 311, app: #Ecto.Association.NotLoaded<association :app is not loaded>, inserted_at: ~N[2025-09-12 17:41:28], updated_at: ~N[2025-09-12 17:41:28]}, locale: "en", follower: #Pleroma.User<__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: "AQFT4gmoLbbW6TrjMG", bio: "A wizard in a world of witches.<br/><br/><a class=\"hashtag\" data-tag=\"nobot\" href=\"https://xyzzy.link/tag/nobot\" rel=\"tag ugc\">#nobot</a> unless you&#39;re friendly :)", raw_bio: nil, name: "wlo", nickname: "wizard@xyzzy.link", password_confirmation: nil, ap_id: "https://xyzzy.link/users/wizard", avatar: %{"type" => "Image", "url" => [%{"href" => "https://s3.us-east-2.wasabisys.com/xyzzylink-media/f9326efcb72cad77fad112debf67d87d8c632244569f705d6abd49c541fd86f6.gif"}]}, local: false, follower_address: "https://xyzzy.link/users/wizard/followers", following_address: "https://xyzzy.link/users/wizard/following", featured_address: "https://xyzzy.link/users/wizard/collections/featured", search_rank: nil, search_type: nil, tags: [], last_refreshed_at: ~N[2026-05-05 22:42:34.386652], last_digest_emailed_at: ~N[2022-12-04 02:03:52], banner: %{"type" => "Image", "url" => [%{"href" => "https://s3.us-east-2.wasabisys.com/xyzzylink-media/75f2fec27bd9815c2b5bd16c5db84a97d319998fdf5af4847925e19e4717c983.jpg"}]}, background: nil, note_count: 5366, follower_count: 987, following_count: 527, is_locked: false, is_confirmed: true, password_reset_pending: false, is_approved: true, registration_reason: nil, confirmation_token: nil, default_scope: "public", domain_blocks: [], is_active: true, no_rich_text: false, is_moderator: false, is_admin: false, show_role: true, mastofe_settings: nil, uri: "https://xyzzy.link/users/wizard", hide_followers_count: false, hide_follows_count: false, hide_followers: true, hide_follows: false, hide_favorites: true, email_notifications: %{"digest" => false}, mascot: nil, emoji: %{}, pleroma_settings_store: %{}, fields: [%{"name" => "AFPS", "value" => "Xonotic; CPMA; UT"}, %{"name" => "Datacenter", "value" => "Raver"}, %{"name" => "Pronoun", "value" => "she"}, %{"name" => "Location", "value" => "Neo England"}, %{"name" => "Website", "value" => "<a href=\"https://willow.phantoma.online\" rel=\"ugc\">https://willow.phantoma.online</a>"}], raw_fields: [], is_discoverable: true, invisible: false, ...>, csp_nonce: "9nbZDGpKafCvCR4", locales: ["en"]}, body_params: %{}, cookies: %{"__Host-pleroma_key" => "SFMyNTY.g3QAAAACbQAAAAtvYXV0aF90b2tlbm0AAAArR0pQMFhDN3ZGMS1nZjBEZ1BYbHk3U0gzeXVkdmNDTUZwei0tdHlhb0tGb20AAAAKb2F1dGhfdXNlcncDbmls.HQSC4WmbwVQtsadK_YhYCD0L_dZUCrGN1rbAvBadqE8", "userLanguage" => "en-US"}, halted: false, host: "akkoma.trwnh.com", method: "POST", owner: #PID<0.146343585.0>, params: %{id: "AQFT4gmoLbbW6TrjMG"}, path_info: ["api", "v1", "follow_requests", "AQFT4gmoLbbW6TrjMG", "authorize"], path_params: %{"id" => "AQFT4gmoLbbW6TrjMG"}, port: 80, private: %{:phoenix_view => %{"html" => Pleroma.Web.MastodonAPI.FollowRequestView, "json" => Pleroma.Web.MastodonAPI.FollowRequestView}, :open_api_spex => %{params: %{id: "AQFT4gmoLbbW6TrjMG"}, body_params: %{}, spec_module: Pleroma.Web.ApiSpec}, Pleroma.Web.Router => [], :phoenix_action => :authorize, :phoenix_controller => Pleroma.Web.MastodonAPI.FollowRequestController, :operation_id => "FollowRequestController.authorize", :phoenix_endpoint => Pleroma.Web.Endpoint, :called_plugs => [Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug, Pleroma.Web.Plugs.EnsureAuthenticatedPlug, Pleroma.Web.Plugs.OAuthScopesPlug], :phoenix_format => "json", :phoenix_router => Pleroma.Web.Router, :plug_session_info => :write, :plug_session_fetch => :done, :plug_session => %{"oauth_token" => "GJP0XC7vF1-gf0DgPXly7SH3yudvcCMFpz--tyaoKFo", "oauth_user" => nil}, :before_send => [#Function<0.76585640/1 in Plug.Session.before_send/2>, #Function<1.94339782/1 in Plug.Logger.call/2>], :phoenix_layout => %{"html" => {Pleroma.Web.LayoutView, :app}}}, query_params: %{}, query_string: "", remote_ip: {9728, 5888, 29952, 38560, 0, 0, 0, 2115}, req_cookies: %{"__Host-pleroma_key" => "SFMyNTY.g3QAAAACbQAAAAtvYXV0aF90b2tlbm0AAAArR0pQMFhDN3ZGMS1nZjBEZ1BYbHk3U0gzeXVkdmNDTUZwei0tdHlhb0tGb20AAAAKb2F1dGhfdXNlcncDbmls.HQSC4WmbwVQtsadK_YhYCD0L_dZUCrGN1rbAvBadqE8", "userLanguage" => "en-US"}, req_headers: [{"accept", "*/*"}, {"accept-encoding", "gzip, deflate, br, zstd"}, ...], ...}, nil) May 06 06:39:55 trwnh.com mix[1145700]: (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:5: Pleroma.Web.MastodonAPI.FollowRequestController.action/2 May 06 06:39:55 trwnh.com mix[1145700]: (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:5: Pleroma.Web.MastodonAPI.FollowRequestController.phoenix_controller_pipeline/2 May 06 06:39:55 trwnh.com mix[1145700]: (phoenix 1.8.1) lib/phoenix/router.ex:416: Phoenix.Router.__call__/5 May 06 06:39:55 trwnh.com mix[1145700]: (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/endpoint.ex:5: Pleroma.Web.Endpoint.plug_builder_call/2 May 06 06:39:55 trwnh.com mix[1145700]: (pleroma 3.18.0-248-g479c7288-develop) lib/pleroma/web/endpoint.ex:5: Pleroma.Web.Endpoint.call/2 May 06 06:39:55 trwnh.com mix[1145700]: (plug_cowboy 2.7.4) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2 May 06 06:39:55 trwnh.com mix[1145700]: (cowboy 2.14.2) /srv/akkoma/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2 ``` ### Severity I cannot use the software ### Have you searched for this issue? - [ ] I have double-checked and have not found this issue mentioned anywhere.
a added the
bug
label 2026-05-06 07:56:19 +00:00
Author
Contributor

context

non-working nginx config line

server_name media.akkoma.trwnh.com akkoma.trwnh.com;

working nginx config line

server_name akkoma.trwnh.com media.akkoma.trwnh.com;

the problem

nginx $server_name variable only gets set once per server block, NOT based on the Host header of the request

buggy http to https redirect

return 301 https://$server_name$request_uri;

correct http to https redirect

return 301 https://$host$request_uri;

summary

because akkoma's suggested nginx config uses the same server block for both the instance and the media subdomain, $server_name will cause issues and the correct nginx variable is actually $host

external requests are generally made to https and will generally always work

internal requests are made to http / port 80 and were getting redirected to the media subdomain (since it was the first one declared in the server block)

somehow the only noticeable side effect for me was that 1 specific remote user's follow requests would always fail even after i accepted them (although other users on the same remote instance were accepted successfully)

suggested solutions

option 1 is to put out a psa and note prominently in the nginx config that server_name should put the instance domain first and the media domain second. but this does still have the issue of http media redirecting to https instance (not ideal solution)

option 2 is to change the nginx config to use separate server blocks for the instance domain and the media domain, then put out a psa that the nginx config has changed. this repeats the same server code block but fixes the issue without additional issues

option 3 is to change the nginx config to use $host instead of $server_name because really that is what it should have always been. then put out a psa about this so people can check their configs in case they've been silently affected this whole time like me

i recommend option 3

## context ### non-working nginx config line server_name media.akkoma.trwnh.com akkoma.trwnh.com; ### working nginx config line server_name akkoma.trwnh.com media.akkoma.trwnh.com; ## the problem nginx $server_name variable only gets set once per server block, **NOT** based on the Host header of the request ### buggy http to https redirect return 301 https://$server_name$request_uri; ### correct http to https redirect return 301 https://$host$request_uri; ## summary because akkoma's suggested nginx config uses the same server block for both the instance and the media subdomain, $server_name will cause issues and the correct nginx variable is actually $host external requests are generally made to https and will generally always work internal requests are made to http / port 80 and were getting redirected to the media subdomain (since it was the first one declared in the server block) somehow the only noticeable side effect for me was that 1 specific remote user's follow requests would always fail even after i accepted them (although other users on the same remote instance were accepted successfully) ### suggested solutions option 1 is to put out a psa and note prominently in the nginx config that server_name should put the instance domain first and the media domain second. but this does still have the issue of http media redirecting to https instance (not ideal solution) option 2 is to change the nginx config to use separate server blocks for the instance domain and the media domain, then put out a psa that the nginx config has changed. this repeats the same server code block but fixes the issue without additional issues option 3 is to change the nginx config to use $host instead of $server_name because really that is what it should have always been. then put out a psa about this so people can check their configs in case they've been silently affected this whole time like me i recommend option 3
Owner

nginx $server_name variable only gets set once per server block, NOT based on the Host header of the request

This is an actual issue, although if the template is used it only means if someone takes a media URL and on their own decides to replace https with http it won’t resolve.

working nginx config line

The template already puts the main domain first.

internal requests are made to http / port 80 […]

There is no such thing as “internal [HTTP] requests”. Akkoma never ever performs AP fetches on its own domain (and if it somehow tries anyway, a safety check will make it error out early), nor does it make REST API requests to itself (except of course in the test suite). I have no idea how you got this idea.

Your logs also do not show anything using a http:// scheme in queries or data.

There’s the Server: domain:80 (http) line, but this seems unrelated to actual port 80 usage; it shows up in my logs too eventhough the redirect was already correct and Akkoma does not listen on port 80 at all, nor is Akkoma’s ports accessible to the outside. Might be phoenix just notices the (internal) http usage and autocompletes the port to the default value.
Either way, this still wouldn’t make sense since even the broken redirect should prevent anything from being able to access Akkoma itself via port 80.
I do not see any port: 80 bits in my logs though. Are you listening on actual port 80 (in a contianer / on a different IP so it doesn’t conflict with nginx port 80 usage)?

In any event, your Follow-request issue is most certainly unrelated to the nginx HTTP redirect.
The match error suggest it encountered some unexpected error while trying to update the FR state and federating the accept out.

Check the state / data of both the follow request activity in the activities table and the following_relationships table. For unfathomable reasons the state is tracked at both of these places and most likely something desynced.
Alrenatively, reject the FR request and temporarily block the other user. This should clear out all FR states. After unblock a new FR will likely just work.


Unrelated to the issue, but you just publlished your OAuth token as part of the debug logs.
You’ll want to log out/in again and go to Settings → Security and invalidate the leaked token if still there.

And you are lacking two security releases behind.

> nginx $server_name variable only gets set once per server block, NOT based on the Host header of the request This is an actual issue, although if the template is used it only means if someone takes a media URL and on their own decides to replace `https` with `http` it won’t resolve. > working nginx config line The template already puts the main domain first. > internal requests are made to http / port 80 […] There is no such thing as “internal [HTTP] requests”. Akkoma never ever performs AP fetches on its own domain *(and if it somehow tries anyway, a safety check will make it error out early)*, nor does it make REST API requests to itself (except of course in the test suite). I have no idea how you got this idea. Your logs also do not show anything using a `http://` scheme in queries or data. There’s the `Server: domain:80 (http)` line, but this seems unrelated to actual port 80 usage; it shows up in my logs too eventhough the redirect was already correct and Akkoma does not listen on port 80 at all, nor is Akkoma’s ports accessible to the outside. Might be phoenix just notices the (internal) `http` usage and autocompletes the port to the default value. Either way, this still wouldn’t make sense since even the broken redirect should prevent anything from being able to access Akkoma itself via port 80. I do not see any `port: 80` bits in my logs though. Are you listening on actual port 80 (in a contianer / on a different IP so it doesn’t conflict with nginx port 80 usage)? In any event, your Follow-request issue is most certainly unrelated to the nginx HTTP redirect. The match error suggest it encountered some unexpected error while trying to update the FR state and federating the accept out. Check the state / data of both the follow request activity in the `activities` table _and_ the `following_relationships` table. For unfathomable reasons the state is tracked at _both_ of these places and most likely something desynced. Alrenatively, reject the FR request and temporarily block the other user. This should clear out all FR states. After unblock a new FR will likely just work. ---- Unrelated to the issue, but you just publlished your OAuth token as part of the debug logs. You’ll want to log out/in again and go to `Settings → Security` and invalidate the leaked token if still there. And you are lacking two security releases behind.
Oneric changed title from [bug] nginx config uses $server_name and multiple server_name, leading to broken http->https redirects on internal requests to Cannot accept follow request 2026-05-06 14:20:32 +00:00
Author
Contributor

unrelated to the issue

logs were taken before an update so i'm no longer behind. also i cleared my tokens, thank you for the heads-up -- i wasn't even aware that tokens were logged


Akkoma never ever performs AP fetches on its own domain [...] nor does it make REST API requests to itself [...] I have no idea how you got this idea

This line of reasoning was based entirely on the Server: akkoma.trwnh.com:80 (http) line, which made it seem to me that Phoenix / Cowboy was trying to access the domain over http instead of accessing localhost + the configured port. I guess it was a red herring, since it was at the top of the most important-looking block (the stack trace of the error itself), but as you say,

Might be phoenix just notices the (internal) http usage and autocompletes the port to the default value.

Either way, this still wouldn’t make sense since even the broken redirect should prevent anything from being able to access Akkoma itself via port 80.

As for my own usage,

Are you listening on actual port 80 (in a contianer / on a different IP so it doesn’t conflict with nginx port 80 usage)?

No, my config uses this in prod.secret.exs:

config :pleroma, Pleroma.Web.Endpoint,
  url: [host: "akkoma.trwnh.com", scheme: "https", port: 443],
  http: [ip: {127, 0, 0, 1}, port: 2556]

your Follow-request issue is most certainly unrelated to the nginx HTTP redirect

it's weird that the follow request managed to be successfully accepted after i changed this, though! my procedure with testing this:

  • have the user send me a follow request
  • note the frq notification on my end
  • click accept
  • the notification changes in-place to a new follower notification
  • refresh the page
  • the notification has disappeared and the state has gone back to not following me

then i changed the order of the server_name values so that akkoma.trwnh.com was first (before media.akkoma.trwnh.com)

  • have the user send me a follow request
  • note the frq notification on my end
  • click accept
  • the notification changes in-place to a new follower notification
  • refresh the page
  • it seemingly works this time, the user is following me, i make a post and it shows up in their home timeline for the 1st time in years

so that's what got me going down the rabbit hole of the nginx config -- the observation that it seemingly fixed the issue!

now, i say "seemingly", because after some indeterminate time of the follow relationship being successfully established, it looks like it's undone itself at some point within the time window starting a few minutes after accepting the request and ending about two hours later.

Alternatively, reject the FR request and temporarily block the other user. This should clear out all FR states. After unblock a new FR will likely just work.

i've tried this in the past to no success, but haven't tried this recently (yesterday). i guess i can try to coordinate this during a time when the other person is active so i don't end up making it worse by no longer following them and missing their posts.

Check the state / data of both the follow request activity in the activities table and the following_relationships table. For unfathomable reasons the state is tracked at both of these places and most likely something desynced.

here's what i'm seeing:

there is no Follow activity in the activities table whatsoever for the affected user.

akkoma=# select * from activities where (activities.actor = 'https://xyzzy.link/users/<someone else>' and activities.data->>'type' = 'Follow' );
 00000194-18cb-5fba-5ec8-d96a81310000 | {"cc": [], "id": "https://xyzzy.link/activities/c142ea32-7843-4143-88be-30b1fa015e00", "to": ["https://akkoma.trwnh.com/users/a"], "bcc": [], "bto": [], "type": "Follow", "actor": "https://xyzzy.link/users/<someone else>", "st
ate": "accept", "object": "https://akkoma.trwnh.com/users/a"} | 2024-12-30 18:19:29 | 2024-12-30 18:19:29 | f     | https://xyzzy.link/users/<someone else> | {https://akkoma.trwnh.com/users/a}

akkoma=# select * from activities where (activities.actor = 'https://xyzzy.link/users/wizard' and activities.data->>'type' = 'Follow' );

akkoma=#

meanwhile for following_relationships i don't know how to make sense of this since the ids don't seem to correlate to anything else?

akkoma=# select * from following_relationships limit 3;
  80 | 00000184-d547-a2f2-8f29-638d0a060000 | 0000018c-9ec2-fbfc-792d-50f8e6500000 |     2 | 2023-12-25 02:17:15 | 2023-12-25 08:46:23
  57 | 00000184-d547-a2f2-8f29-638d0a060000 | 0000018a-7257-4e22-016b-794c0c410000 |     2 | 2023-10-31 06:38:41 | 2023-10-31 06:38:44
 136 | 00000184-d547-a2f2-8f29-638d0a060000 | 00000184-f316-0b03-b79d-88733ca00000 |     2 | 2023-12-25 03:21:54 | 2023-12-25 12:53:01

most of them have state=2, and a few have state=1. the timestamps range from 2023 to 2026-04-30.

i can guess my own id at least:

akkoma=# select count(*) from following_relationships where (following_id = '00000184-d547-a2f2-8f29-638d0a060000');
   <the number of followers i have>

and all of those have state=2 which seems expected

akkoma=# select count(*) from following_relationships where (following_id = '00000184-d547-a2f2-8f29-638d0a060000' and state = 1);
     0

akkoma=# select count(*) from following_relationships where (following_id = '00000184-d547-a2f2-8f29-638d0a060000' and state = 2);
     <the number of followers i have>

i'm not sure how to convert an https id to a follower_id in the following_relationships table.

but after poking around again in the activities table, it seems the latest 2 Accept activities are still there?

akkoma=# select * from activities where (activities.actor = 'https://akkoma.trwnh.com/users/a' and activities.data->>'type' = 'Accept' and activities.recipients = '{https://xyzzy.link/users/wizard}');
 0000019d-fc30-d15a-b9c2-90e7dba60000 | {"cc": [], "id": "https://akkoma.trwnh.com/activities/7a720eeb-2a6e-4830-b61b-4666156cd598", "to": ["https://xyzzy.link/users/wizard"], "bcc": [], "bto": [], "type": "Accept", "actor": "https://akkoma.trwnh.com/user
s/a", "object": "https://xyzzy.link/activities/e16ae88b-c4ce-4b07-a74a-7228d94951ed"} | 2026-05-06 07:29:11 | 2026-05-06 07:29:11 | t     | https://akkoma.trwnh.com/users/a | {https://xyzzy.link/users/wizard}
 0000019d-fc32-ab15-b9c2-90e7dba60000 | {"cc": [], "id": "https://akkoma.trwnh.com/activities/787b13ad-7947-4749-8f2e-3490d986e5b2", "to": ["https://xyzzy.link/users/wizard"], "bcc": [], "bto": [], "type": "Accept", "actor": "https://akkoma.trwnh.com/user
s/a", "object": "https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d"} | 2026-05-06 07:31:12 | 2026-05-06 07:31:12 | t     | https://akkoma.trwnh.com/users/a | {https://xyzzy.link/users/wizard}

akkoma=#

i'm not sure why there are 2 of them, or why only the latest 2.

{
  "cc": [],
  "id": "https://akkoma.trwnh.com/activities/7a720eeb-2a6e-4830-b61b-4666156cd598",
  "to": [
    "https://xyzzy.link/users/wizard"
  ],
  "bcc": [],
  "bto": [],
  "type": "Accept",
  "actor": "https://akkoma.trwnh.com/users/a",
  "object": "https://xyzzy.link/activities/e16ae88b-c4ce-4b07-a74a-7228d94951ed"
}
{
  "cc": [],
  "id": "https://akkoma.trwnh.com/activities/787b13ad-7947-4749-8f2e-3490d986e5b2",
  "to": [
    "https://xyzzy.link/users/wizard"
  ],
  "bcc": [],
  "bto": [],
  "type": "Accept",
  "actor": "https://akkoma.trwnh.com/users/a",
  "object": "https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d"
}

for what it's worth, my activities table doesn't contain either of the referenced Follow activities, while in the case of other Accept activities I am able to find the accepted Follow in my activities table.

akkoma=# select * from activities where (activities.data->>'id' = 'https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d');

akkoma=# select * from activities where (activities.data->>'id' = 'https://xyzzy.link/activities/e16ae88b-c4ce-4b07-a74a-7228d94951ed');

akkoma=# select * from activities where (activities.data->>'id' = 'https://ihatebeinga.live/activities/cae0eb56-cf1b-4da3-9b95-2162868a88de');
 0000018f-1748-3997-4781-613a56210000 | {"cc": [], "id": "https://ihatebeinga.live/activities/cae0eb56-cf1b-4da3-9b95-2162868a88de", "to": ["https://akkoma.trwnh.com/users/a"], "bcc": [], "bto": [], "type": "Follow", "actor": "https://ihatebeinga.live/use
rs/FloatingGhost", "state": "accept", "object": "https://akkoma.trwnh.com/users/a"} | 2024-04-25 22:02:40 | 2024-04-25 22:02:40 | f     | https://ihatebeinga.live/users/FloatingGhost | {https://akkoma.trwnh.com/users/a}
> unrelated to the issue logs were taken before an update so i'm no longer behind. also i cleared my tokens, thank you for the heads-up -- i wasn't even aware that tokens were logged --- > Akkoma never ever performs AP fetches on its own domain [...] nor does it make REST API requests to itself [...] I have no idea how you got this idea This line of reasoning was based entirely on the `Server: akkoma.trwnh.com:80 (http)` line, which made it seem to me that Phoenix / Cowboy was trying to access the domain over http instead of accessing localhost + the configured port. I guess it was a red herring, since it was at the top of the most important-looking block (the stack trace of the error itself), but as you say, > Might be phoenix just notices the (internal) http usage and autocompletes the port to the default value. > Either way, this still wouldn’t make sense since even the broken redirect should prevent anything from being able to access Akkoma itself via port 80. As for my own usage, > Are you listening on actual port 80 (in a contianer / on a different IP so it doesn’t conflict with nginx port 80 usage)? No, my config uses this in prod.secret.exs: ``` config :pleroma, Pleroma.Web.Endpoint, url: [host: "akkoma.trwnh.com", scheme: "https", port: 443], http: [ip: {127, 0, 0, 1}, port: 2556] ``` --- > your Follow-request issue is most certainly unrelated to the nginx HTTP redirect it's weird that the follow request managed to be successfully accepted after i changed this, though! my procedure with testing this: - have the user send me a follow request - note the frq notification on my end - click accept - the notification changes in-place to a new follower notification - refresh the page - the notification has disappeared and the state has gone back to not following me then i changed the order of the server_name values so that akkoma.trwnh.com was first (before media.akkoma.trwnh.com) - have the user send me a follow request - note the frq notification on my end - click accept - the notification changes in-place to a new follower notification - refresh the page - it seemingly works this time, the user is following me, i make a post and it shows up in their home timeline for the 1st time in years so that's what got me going down the rabbit hole of the nginx config -- the observation that it *seemingly fixed the issue*! now, i say "seemingly", because after some indeterminate time of the follow relationship being successfully established, it looks like it's undone itself at some point within the time window starting a few minutes after accepting the request and ending about two hours later. > Alternatively, reject the FR request and temporarily block the other user. This should clear out all FR states. After unblock a new FR will likely just work. i've tried this in the past to no success, but haven't tried this recently (yesterday). i guess i can try to coordinate this during a time when the other person is active so i don't end up making it worse by no longer following them and missing their posts. > Check the state / data of both the follow request activity in the `activities` table *and* the `following_relationships` table. For unfathomable reasons the state is tracked at *both* of these places and most likely something desynced. here's what i'm seeing: there is no Follow activity in the `activities` table whatsoever for the affected user. ```sql akkoma=# select * from activities where (activities.actor = 'https://xyzzy.link/users/<someone else>' and activities.data->>'type' = 'Follow' ); 00000194-18cb-5fba-5ec8-d96a81310000 | {"cc": [], "id": "https://xyzzy.link/activities/c142ea32-7843-4143-88be-30b1fa015e00", "to": ["https://akkoma.trwnh.com/users/a"], "bcc": [], "bto": [], "type": "Follow", "actor": "https://xyzzy.link/users/<someone else>", "st ate": "accept", "object": "https://akkoma.trwnh.com/users/a"} | 2024-12-30 18:19:29 | 2024-12-30 18:19:29 | f | https://xyzzy.link/users/<someone else> | {https://akkoma.trwnh.com/users/a} akkoma=# select * from activities where (activities.actor = 'https://xyzzy.link/users/wizard' and activities.data->>'type' = 'Follow' ); akkoma=# ``` meanwhile for `following_relationships` i don't know how to make sense of this since the ids don't seem to correlate to anything else? ```sql akkoma=# select * from following_relationships limit 3; 80 | 00000184-d547-a2f2-8f29-638d0a060000 | 0000018c-9ec2-fbfc-792d-50f8e6500000 | 2 | 2023-12-25 02:17:15 | 2023-12-25 08:46:23 57 | 00000184-d547-a2f2-8f29-638d0a060000 | 0000018a-7257-4e22-016b-794c0c410000 | 2 | 2023-10-31 06:38:41 | 2023-10-31 06:38:44 136 | 00000184-d547-a2f2-8f29-638d0a060000 | 00000184-f316-0b03-b79d-88733ca00000 | 2 | 2023-12-25 03:21:54 | 2023-12-25 12:53:01 ``` most of them have state=2, and a few have state=1. the timestamps range from 2023 to 2026-04-30. i can guess my own id at least: ```sql akkoma=# select count(*) from following_relationships where (following_id = '00000184-d547-a2f2-8f29-638d0a060000'); <the number of followers i have> ``` and all of those have state=2 which seems expected ```sql akkoma=# select count(*) from following_relationships where (following_id = '00000184-d547-a2f2-8f29-638d0a060000' and state = 1); 0 akkoma=# select count(*) from following_relationships where (following_id = '00000184-d547-a2f2-8f29-638d0a060000' and state = 2); <the number of followers i have> ``` i'm not sure how to convert an https id to a `follower_id` in the `following_relationships` table. but after poking around again in the `activities` table, it seems the latest 2 Accept activities are still there? ```sql akkoma=# select * from activities where (activities.actor = 'https://akkoma.trwnh.com/users/a' and activities.data->>'type' = 'Accept' and activities.recipients = '{https://xyzzy.link/users/wizard}'); 0000019d-fc30-d15a-b9c2-90e7dba60000 | {"cc": [], "id": "https://akkoma.trwnh.com/activities/7a720eeb-2a6e-4830-b61b-4666156cd598", "to": ["https://xyzzy.link/users/wizard"], "bcc": [], "bto": [], "type": "Accept", "actor": "https://akkoma.trwnh.com/user s/a", "object": "https://xyzzy.link/activities/e16ae88b-c4ce-4b07-a74a-7228d94951ed"} | 2026-05-06 07:29:11 | 2026-05-06 07:29:11 | t | https://akkoma.trwnh.com/users/a | {https://xyzzy.link/users/wizard} 0000019d-fc32-ab15-b9c2-90e7dba60000 | {"cc": [], "id": "https://akkoma.trwnh.com/activities/787b13ad-7947-4749-8f2e-3490d986e5b2", "to": ["https://xyzzy.link/users/wizard"], "bcc": [], "bto": [], "type": "Accept", "actor": "https://akkoma.trwnh.com/user s/a", "object": "https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d"} | 2026-05-06 07:31:12 | 2026-05-06 07:31:12 | t | https://akkoma.trwnh.com/users/a | {https://xyzzy.link/users/wizard} akkoma=# ``` i'm not sure why there are 2 of them, or why only the latest 2. ```json { "cc": [], "id": "https://akkoma.trwnh.com/activities/7a720eeb-2a6e-4830-b61b-4666156cd598", "to": [ "https://xyzzy.link/users/wizard" ], "bcc": [], "bto": [], "type": "Accept", "actor": "https://akkoma.trwnh.com/users/a", "object": "https://xyzzy.link/activities/e16ae88b-c4ce-4b07-a74a-7228d94951ed" } ``` ```json { "cc": [], "id": "https://akkoma.trwnh.com/activities/787b13ad-7947-4749-8f2e-3490d986e5b2", "to": [ "https://xyzzy.link/users/wizard" ], "bcc": [], "bto": [], "type": "Accept", "actor": "https://akkoma.trwnh.com/users/a", "object": "https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d" } ``` for what it's worth, my `activities` table doesn't contain either of the referenced Follow activities, while in the case of other Accept activities I *am* able to find the accepted Follow in my `activities` table. ```sql akkoma=# select * from activities where (activities.data->>'id' = 'https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d'); akkoma=# select * from activities where (activities.data->>'id' = 'https://xyzzy.link/activities/e16ae88b-c4ce-4b07-a74a-7228d94951ed'); akkoma=# select * from activities where (activities.data->>'id' = 'https://ihatebeinga.live/activities/cae0eb56-cf1b-4da3-9b95-2162868a88de'); 0000018f-1748-3997-4781-613a56210000 | {"cc": [], "id": "https://ihatebeinga.live/activities/cae0eb56-cf1b-4da3-9b95-2162868a88de", "to": ["https://akkoma.trwnh.com/users/a"], "bcc": [], "bto": [], "type": "Follow", "actor": "https://ihatebeinga.live/use rs/FloatingGhost", "state": "accept", "object": "https://akkoma.trwnh.com/users/a"} | 2024-04-25 22:02:40 | 2024-04-25 22:02:40 | f | https://ihatebeinga.live/users/FloatingGhost | {https://akkoma.trwnh.com/users/a} ```
Author
Contributor

if i had to guess, something is causing the Follow to be deleted from activities, so even if the Accept remains, it references a no-longer-existing Follow activity.

if i had to guess, something is causing the Follow to be deleted from `activities`, so even if the Accept remains, it references a no-longer-existing Follow activity.
Owner

there is no Follow activity in the activities table whatsoever for the affected user.

And that’s how you end up with a nil in the error clause which won't match anything. So indeed a desync as suspected.

> there is no Follow activity in the activities table whatsoever for the affected user. And that’s how you end up with a `nil` in the error clause which won't match anything. So indeed a desync as suspected.
Owner

sorry, forgot to reply to this bit:

i'm not sure how to convert an https id to a follower_id in the following_relationships table.

follow*_ids are referencing the primary ID of users in the database also matching their API id (but PostgreSQL doesn’t understand the base62 encoding directly; you’ll need to convert to hex).
You can look up the primary key of a user in the users table, like SELECT id FROM users WHERE ap_id = 'https://…'; or use a JOIN users AS u ON follower_id = u.id etc to get human-friendly output when inspecting the followers relation table.

The menaing of the state enum values can be found in lib/pleroma/ecto_enums.ex:

defenum(Pleroma.FollowingRelationship.State,
  follow_pending: 1,
  follow_accept: 2,
  follow_reject: 3
)

iirc follow_rejects don’t survive long and get cleared out after a while (same for activities of rejected FRs)

sorry, forgot to reply to this bit: > i'm not sure how to convert an https id to a follower_id in the following_relationships table. `follow*_id`s are referencing the primary ID of users in the database also matching their API id *(but PostgreSQL doesn’t understand the base62 encoding directly; you’ll need to convert to hex)*. You can look up the primary key of a user in the `users` table, like `SELECT id FROM users WHERE ap_id = 'https://…';` or use a `JOIN users AS u ON follower_id = u.id` etc to get human-friendly output when inspecting the followers relation table. The menaing of the state enum values can be found in `lib/pleroma/ecto_enums.ex`: ```elixir defenum(Pleroma.FollowingRelationship.State, follow_pending: 1, follow_accept: 2, follow_reject: 3 ) ``` iirc `follow_reject`s don’t survive long and get cleared out after a while (same for activities of rejected FRs)
Author
Contributor

I'm wondering if it makes sense to query for Accept activities whose object isn't present in the activities table, and then if it makes sense to purge such Accept activities, or purge follow relationships based on such Accept activities?

The problem is that Accept activities can be used for objects that aren't a Follow activity, so this might not be a safe operation.

I'm wondering if it makes sense to query for Accept activities whose object isn't present in the `activities` table, and then if it makes sense to purge such Accept activities, or purge follow relationships based on such Accept activities? The problem is that Accept activities can be used for objects that aren't a Follow activity, so this might not be a safe operation.
Owner

I don’t know if Accepts are already pruned too when a FR is rejected or the accept Undone. If not, yes some space could be saved in theory by getting rid of them.
But either way, I don’t think this is related to the desync issue you experienced. There shouldn’ŧ be any issue with multiple Accepts.

I don’t know if Accepts are already pruned too when a FR is rejected or the accept `Undo`ne. If not, yes some space could be saved in theory by getting rid of them. But either way, I don’t think this is related to the desync issue you experienced. There shouldn’ŧ be any issue with multiple Accepts.
Author
Contributor

There's two aspects here:

  1. How to recover from the issue = what I'm trying to figure out in that comment. Trying to do a block-unblock on both sides requires action on both sides, and I'm wondering if there's anything that can be done to ameliorate the situation on one side. And I think there might be, since the issue is manifesting on my side -- the Follow seems to be successfully sent from the remote instance, and successfully received on my local side, but the Follow disappears later, and if the Accept fails locally and is never sent to the remote instance, then there isn't really a "desync" per se -- both instances agree that that the other user isn't following me. There's only a desync if the Accept is delivered to the other instance, and it's not getting that far in most cases.
  2. How to prevent the issue from happening = idk what causes it, so I can't say more about this right now. I'm hoping by investigating more, something might show up.

The problem is I don't know exactly what to test/query for. All I have managed to reveal so far is that there are Accept activities whose object isn't known locally. Ideally you would be able to fetch the object, but in the case of Follow activities they aren't fetchable on some implementations, and it's not guaranteed in general that all objects are fetchable. I guess it's fine to continue storing them, but they are kind of "reverse orphaned".

Maybe what I should be examining is the relation between Follow activities and following_relationships, which is where the JOIN statement can come in handy I guess. or otherwise:

select id from users where ap_id = 'https://akkoma.trwnh.com/users/a'; 
-- 00000184-d547-a2f2-8f29-638d0a060000

select id from users where ap_id = 'https://xyzzy.link/users/wizard';
-- 00000184-dae0-1fe3-b79d-88733ca00000

select * from following_relationships where (follower_id = '00000184-dae0-1fe3-b79d-88733ca00000' and following_id = '00000184-d547-a2f2-8f29-638d0a060000'); 
-- (0 rows)

So the following relationship between the remote user and my account doesn't exist in the following_relationships table.

And as previously established, the Follow activities don't exist in the activities table, either... anymore?

select * from activities where (activities.actor = 'https://xyzzy.link/users/wizard' and activities.data->>'type' = 'Follow' ); 
-- (0 rows)

And it seems reasonable to assume that the reason that trying to accept the follow request immediately fails is probably something like this:

  • the follow request controller tries to accept the follow request
  • in order to accept the follow request, we need to generate an Accept Follow
  • ERROR: the follow request doesn't have an associated Follow activity!
  • therefore, the Accept Follow cannot be generated
  • therefore, the follow request is destroyed without being successfully accepted and without sending an Accept activity to the remote user

If I'm wrong about any of that, please correct me.

If that's true, then why might the Follow activity be disappearing on my end? I'll try to get my friend to send another follow request, which I will leave pending, and then run more SQL queries to see what happens.

There's two aspects here: 1) How to recover from the issue = what I'm trying to figure out in that comment. Trying to do a block-unblock on both sides requires action on both sides, and I'm wondering if there's anything that can be done to ameliorate the situation on one side. And I think there might be, since the issue is manifesting on my side -- the Follow seems to be successfully sent from the remote instance, and successfully received on my local side, but the Follow disappears later, and if the Accept fails locally and is never sent to the remote instance, then there isn't really a "desync" per se -- both instances agree that that the other user isn't following me. There's only a desync if the Accept is delivered to the other instance, and it's not getting that far in most cases. 2) How to prevent the issue from happening = idk what causes it, so I can't say more about this right now. I'm hoping by investigating more, something might show up. The problem is I don't know exactly what to test/query for. All I have managed to reveal so far is that there are Accept activities whose object isn't known locally. Ideally you would be able to fetch the object, but in the case of Follow activities they aren't fetchable on some implementations, and it's not guaranteed in general that all objects are fetchable. I guess it's fine to continue storing them, but they are kind of "reverse orphaned". Maybe what I should be examining is the relation between Follow `activities` and `following_relationships`, which is where the JOIN statement can come in handy I guess. or otherwise: ```sql select id from users where ap_id = 'https://akkoma.trwnh.com/users/a'; -- 00000184-d547-a2f2-8f29-638d0a060000 select id from users where ap_id = 'https://xyzzy.link/users/wizard'; -- 00000184-dae0-1fe3-b79d-88733ca00000 select * from following_relationships where (follower_id = '00000184-dae0-1fe3-b79d-88733ca00000' and following_id = '00000184-d547-a2f2-8f29-638d0a060000'); -- (0 rows) ``` So the following relationship between the remote user and my account doesn't exist in the `following_relationships` table. And as previously established, the Follow activities don't exist in the `activities` table, either... anymore? ```sql select * from activities where (activities.actor = 'https://xyzzy.link/users/wizard' and activities.data->>'type' = 'Follow' ); -- (0 rows) ``` And it seems reasonable to assume that the reason that trying to accept the follow request immediately fails is probably something like this: - the follow request controller tries to accept the follow request - in order to accept the follow request, we need to generate an Accept Follow - ERROR: the follow request doesn't have an associated Follow activity! - therefore, the Accept Follow cannot be generated - therefore, the follow request is destroyed without being successfully accepted and without sending an Accept activity to the remote user If I'm wrong about any of that, please correct me. If that's true, then why might the Follow activity be disappearing on my end? I'll try to get my friend to send another follow request, which I will leave pending, and then run more SQL queries to see what happens.
Owner

then there isn't really a "desync" per se

The desync I’m speaking of is between your follow_relationships and your activities table. As the processing works now, all entries in the former MUST have an associated Follow and depending on state also Accept or Reject activity in the latter and the state recorded in the dedicated table and inlined into the Follow activitiy MUST match. If only the activity, or only the relationship entry exist (or both exist with different state etc) then the tables are desynced. Only action on your end should be needed to wipe this clean.

So the following relationship between the remote user and my account doesn't exist in the following_relationships table.

If as you say you still see the FR popping up in API responses, this seems literally impossible. The FR index API endpoint simply scans the follow_relationships table, nothing else (accepting a FR however, needs the activity too to change its state).

def get_follow_requests_query(%User{id: id}) do
__MODULE__
|> join(:inner, [r], f in assoc(r, :follower), as: :follower)
|> where([r], r.state == ^:follow_pending)
|> where([r], r.following_id == ^id)
|> where([r, follower: f], f.is_active == true)
end
def get_follow_requesting_users_with_request_id(%User{} = user) do
get_follow_requests_query(user)
|> select([r, follower: f], %{id: r.id, entry: f})
end

  • therefore, the follow request is destroyed without being successfully accepted and without sending an Accept activity to the remote user

If I'm wrong about any of that, please correct me.

It indeed first tries to find the Follow activity, but upon failure simply stops (and ends up without a matching error handling case). It won't ever touch the relationship table at all. Thus nothing gets "destroyed".
If such a desync occurs, the missing entry most likely was simply never persisted to the db due to e.g. the questionable transaction split in side effect handling, also mentioned in akkkoma#888 and some fatal error occuring between the two or in the later stage.

> then there isn't really a "desync" per se The desync I’m speaking of is between your `follow_relationships` and your `activities` table. As the processing works now, all entries in the former MUST have an associated `Follow` and depending on state also `Accept` or `Reject` activity in the latter and the `state` recorded in the dedicated table and inlined into the `Follow` activitiy MUST match. If only the activity, or only the relationship entry exist *(or both exist with different state etc)* then the tables are desynced. Only action on your end should be needed to wipe this clean. > So the following relationship between the remote user and my account doesn't exist in the following_relationships table. If as you say you still see the FR popping up in API responses, this seems literally impossible. The FR index API endpoint simply scans the `follow_relationships` table, nothing else *(accepting a FR however, needs the activity too to change its state)*. https://akkoma.dev/AkkomaGang/akkoma/src/commit/22e3792ddbc0212fabf4820a02a1bf6724a6c27d/lib/pleroma/following_relationship.ex#L158-169 > - therefore, the follow request is destroyed without being successfully accepted and without sending an Accept activity to the remote user > > If I'm wrong about any of that, please correct me. It indeed first tries to find the `Follow` activity, but upon failure simply stops (and ends up without a matching error handling case). It won't ever touch the relationship table at all. Thus nothing gets "destroyed". If such a desync occurs, the missing entry most likely was simply never persisted to the db due to e.g. the questionable transaction split in side effect handling, also mentioned in `akkkoma#888` and some fatal error occuring between the two or in the later stage.
Author
Contributor

If as you say you still see the FR popping up in API responses, this is literally impossible.

Maybe streaming / push notifications? That could explain why I see it appear in my notifications, but manually checking the follow requests API shows no pending follow requests, and refreshing the frontend makes it disappear (except that one time when it didn't, and I was able to briefly accept it before it undid itself).

upon failure simply stops (and ends up without a matching error handling case)

Probably worth adding error handling for that case, right? Not sure what that would be, but at the very least logging it and pointing out the Follow activity doesn't exist.

the missing entry most likely was simply never persisted to the db due to e.g. the questionable transaction split in side effect handling, also mentioned in akkoma#888

That seems reasonable -- I guess the thing being destroyed is the notification, not the follow request.

If the Follow isn't being persisted, then I guess it could be failing validation somehow (unlikely, both instances are Akkoma 3.19 here), or after it passes validation, it fails persisting (not sure where in the codebase to look for persisting a Follow activity)

> If as you say you still see the FR popping up in API responses, this is literally impossible. Maybe streaming / push notifications? That could explain why I see it appear in my notifications, but manually checking the follow requests API shows no pending follow requests, and refreshing the frontend makes it disappear (except that one time when it didn't, and I was able to briefly accept it before it undid itself). > upon failure simply stops (and ends up without a matching error handling case) Probably worth adding error handling for that case, right? Not sure what that would be, but at the very least logging it and pointing out the Follow activity doesn't exist. > the missing entry most likely was simply never persisted to the db due to e.g. the questionable transaction split in side effect handling, also mentioned in akkoma#888 That seems reasonable -- I guess the thing being destroyed is the notification, not the follow request. If the Follow isn't being persisted, then I guess it could be failing validation somehow (unlikely, both instances are Akkoma 3.19 here), or after it passes validation, it fails persisting (not sure where in the codebase to look for persisting a Follow activity)
Author
Contributor

Context

OK, managed to capture some more logs: https://gist.github.com/trwnh/ec6101fb698cb936c7c1a187e5d54442

Key terms of interest:

  • https://xyzzy.link/activities/196721e3-68f7-43e9-93d5-9dc296d9f9d9 = the Undo Follow I received when the remote user cancels their pending follow request
  • https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d = the Follow activity being undone as the Undo.object
  • https://xyzzy.link/activities/4d85f170-1cd9-4cd9-9bda-6b5f2dec7b61 = the new Follow activity I received that disappeared
  • AQFT4gmoLbbW6TrjMG = the remote user trying to Follow me
  • AQDDR47on7cS3eqxZQ = my user
  • B68miR5szCpnMgPWZk = the new Follow's activities.id (corresponding to the follow request that gets pushed to notifications)

The first point of interest is in 1.txt:

  • The Undo Follow is unhandled on my end. The error is rather nonspecific and is only described as "[error] Unexpected AP doc error: {:error, :error}", but I assume it's because the Follow is not locally known (since it failed to be persisted).

Then a few seconds later, the stuff in 2.txt happens:

  • 03:09:13.261 -- The Follow arrives via POST /inbox
  • 03:09:13.277 -- Handling the Follow activity begins
  • 03:09:13.290 -- /following collection is refetched for some reason
  • 03:09:13.461 -- /followers collection is refetched for some reason
  • 03:09:13.504 -- Trying to push follow relationship update to... the remote user trying to follow me?
  • 03:09:13.517 -- Insert a follow request notification immediately after this line
  • 03:09:13.519 -- Insert a timeline marker for the notifications timeline immediately after this line
  • 03:09:13.539 -- Push notification to SubwayTooter
  • 03:09:14.262 -- Push notification to Toot!

Some other stuff not as relevant, so I excluded it from the logs:

  • 03:09:15.592 -- GET /api/v1/notifications
  • A bunch of Delete Person activities from mastodon.social
  • More Oban noise

Then, the really interesting part, in 3.txt:

  • 03:09:38.843 -- I guess Oban is picking up a federator_incoming job...
  • 03:09:38.845 -- Trying to handle the incoming AP activity, this time it's https://xyzzy.link/activities/196721e3-68f7-43e9-93d5-9dc296d9f9d9 (the Undo, which failed to handle in 1.txt) instead of https://xyzzy.link/activities/4d85f170-1cd9-4cd9-9bda-6b5f2dec7b61 (the new Follow, should have been handled in 2.txt)... so I guess it's a retry?
  • 03:09:38.849 -- DELETE from "notifications"... why?
  • 03:09:38.850 -- DELETE from "activities"... why? !!![This is where the Follow activity gets purged]!!!
  • 03:09:38.851 -- Committed the SQL transaction
  • 03:09:38.852 -- Query the following_relationships... twice? with the same parameters?
  • 03:09:38.853 -- DELETE from "following_relationships"...
  • 03:09:38.855 -- something to do with my user, where the following_relationships.state == :follow_accept
  • 03:09:38.857 -- refetching the remote user via AP
  • 03:09:38.858 -- get their /following
  • 03:09:38.986 -- get their /followers
  • 03:09:39.014 -- something to do with the remote user, where the following_relationships.state == :follow_accept
  • 03:09:39.014 -- Trying to push follow relationship update to... the remote user trying to follow me, again?
  • 03:09:39.015 -- Querying for a Follow activity from that remote user to my user
  • 03:09:39.017 -- UPDATE the Oban job to mark it as "completed"

After that no more results for the key terms of interest.

So naturally I was interested in what https://xyzzy.link/activities/196721e3-68f7-43e9-93d5-9dc296d9f9d9 was supposed to be, but it wasn't persisted either. Still, looking through the logs, it looks like this is actually the unhandled Undo Follow from 1.txt! It's undoing a Follow with an id of https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d instead of https://xyzzy.link/activities/4d85f170-1cd9-4cd9-9bda-6b5f2dec7b61, so I'm not sure why it would later be (re)processed to Undo a completely different Follow...

The Oban job in 3.txt has an id of bf09e703-3324-4a5f-90fc-c5b3080ff0c8, and it appears in the logs at these points:

  • 03:09:07.630 -- 1.txt, with the unhandled Undo Follow
  • 03:09:13.276 -- 2.txt, with the POST /inbox and the new Follow
  • 03:09:13.532 -- 2.txt, somewhere in the middle of INSERT into "notifications"
  • 03:09:38.843 -- 3.txt, the job gets processed and destroys stuff
  • 03:09:39.024 -- 3.txt, the job is marked as "completed"

Theory

I think this issue is due to out-of-order processing, combined with confusion about Undo.Follow.id for some reason. It looks like the Undo Follow arrives necessarily (to cancel the pending frq), then the Follow gets processed and wiped out 25 seconds later when the Oban job is processed. The deletions occur despite the Undo.Follow.id and Follow.id being different.

If I can accept the follow within ~25 seconds or so (before the Oban job undoes it), I think it successfully sends an Accept Follow to the remote instance. But that's an incredibly tight time window, so the most likely outcome is that the user tries to accept the follow after it has already been undone, leading to a no-op and the nil error observed earlier. Also, even if the Accept Follow goes out, it ends up being a state desync between the two instances, and you can't remove that follower or you might forget that they're following you since they aren't shown as a follower. They should still see your posts if at least 1 other user is locally known to follow you.

## Context OK, managed to capture some more logs: https://gist.github.com/trwnh/ec6101fb698cb936c7c1a187e5d54442 Key terms of interest: - `https://xyzzy.link/activities/196721e3-68f7-43e9-93d5-9dc296d9f9d9` = the Undo Follow I received when the remote user cancels their pending follow request - `https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d` = the Follow activity being undone as the Undo.object - `https://xyzzy.link/activities/4d85f170-1cd9-4cd9-9bda-6b5f2dec7b61` = the new Follow activity I received that disappeared - `AQFT4gmoLbbW6TrjMG` = the remote user trying to Follow me - `AQDDR47on7cS3eqxZQ` = my user - `B68miR5szCpnMgPWZk` = the new Follow's activities.id (corresponding to the follow request that gets pushed to notifications) The first point of interest is in 1.txt: - The Undo Follow is unhandled on my end. The error is rather nonspecific and is only described as "[error] Unexpected AP doc error: {:error, :error}", but I assume it's because the Follow is not locally known (since it failed to be persisted). Then a few seconds later, the stuff in 2.txt happens: - `03:09:13.261` -- The Follow arrives via POST /inbox - `03:09:13.277` -- Handling the Follow activity begins - `03:09:13.290` -- /following collection is refetched for some reason - `03:09:13.461` -- /followers collection is refetched for some reason - `03:09:13.504` -- Trying to push follow relationship update to... the remote user trying to follow me? - `03:09:13.517` -- Insert a follow request notification immediately after this line - `03:09:13.519` -- Insert a timeline marker for the notifications timeline immediately after this line - `03:09:13.539` -- Push notification to SubwayTooter - `03:09:14.262` -- Push notification to Toot! Some other stuff not as relevant, so I excluded it from the logs: - `03:09:15.592` -- GET /api/v1/notifications - A bunch of Delete Person activities from mastodon.social - More Oban noise Then, the really interesting part, in 3.txt: - `03:09:38.843` -- I guess Oban is picking up a federator_incoming job... - `03:09:38.845` -- Trying to handle the incoming AP activity, this time it's `https://xyzzy.link/activities/196721e3-68f7-43e9-93d5-9dc296d9f9d9` (the Undo, which failed to handle in 1.txt) instead of `https://xyzzy.link/activities/4d85f170-1cd9-4cd9-9bda-6b5f2dec7b61` (the new Follow, should have been handled in 2.txt)... so I guess it's a retry? - `03:09:38.849` -- DELETE from "notifications"... why? - `03:09:38.850` -- DELETE from "activities"... why? !!![This is where the Follow activity gets purged]!!! - `03:09:38.851` -- Committed the SQL transaction - `03:09:38.852` -- Query the following_relationships... twice? with the same parameters? - `03:09:38.853` -- DELETE from "following_relationships"... - `03:09:38.855` -- something to do with my user, where the following_relationships.state == :follow_accept - `03:09:38.857` -- refetching the remote user via AP - `03:09:38.858` -- get their /following - `03:09:38.986` -- get their /followers - `03:09:39.014` -- something to do with the remote user, where the following_relationships.state == :follow_accept - `03:09:39.014` -- Trying to push follow relationship update to... the remote user trying to follow me, again? - `03:09:39.015` -- Querying for a Follow activity from that remote user to my user - `03:09:39.017` -- UPDATE the Oban job to mark it as "completed" After that no more results for the key terms of interest. So naturally I was interested in what `https://xyzzy.link/activities/196721e3-68f7-43e9-93d5-9dc296d9f9d9` was supposed to be, but it wasn't persisted either. Still, looking through the logs, it looks like this is actually the unhandled Undo Follow from 1.txt! It's undoing a Follow with an id of `https://xyzzy.link/activities/331b3902-7638-429b-9334-cabadda2b44d` instead of `https://xyzzy.link/activities/4d85f170-1cd9-4cd9-9bda-6b5f2dec7b61`, so I'm not sure why it would later be (re)processed to Undo a completely different Follow... The Oban job in 3.txt has an id of `bf09e703-3324-4a5f-90fc-c5b3080ff0c8`, and it appears in the logs at these points: - `03:09:07.630` -- 1.txt, with the unhandled Undo Follow - `03:09:13.276` -- 2.txt, with the POST /inbox and the new Follow - `03:09:13.532` -- 2.txt, somewhere in the middle of INSERT into "notifications" - `03:09:38.843` -- 3.txt, the job gets processed and destroys stuff - `03:09:39.024` -- 3.txt, the job is marked as "completed" ## Theory I think this issue is due to out-of-order processing, combined with confusion about Undo.Follow.id for some reason. It looks like the Undo Follow arrives necessarily (to cancel the pending frq), then the Follow gets processed and wiped out 25 seconds later when the Oban job is processed. The deletions occur despite the Undo.Follow.id and Follow.id being different. If I can accept the follow within ~25 seconds or so (before the Oban job undoes it), I think it successfully sends an Accept Follow to the remote instance. But that's an incredibly tight time window, so the most likely outcome is that the user tries to accept the follow after it has already been undone, leading to a no-op and the nil error observed earlier. Also, even if the Accept Follow goes out, it ends up being a state desync between the two instances, and you can't remove that follower or you might forget that they're following you since they aren't shown as a follower. They should still see your posts if at least 1 other user is locally known to follow you.
a changed title from Cannot accept follow request to Follow activity gets undone before the follow request can be accepted 2026-05-10 09:03:41 +00:00
a changed title from Follow activity gets undone before the follow request can be accepted to Incoming Follow activity gets undone by Undo of a different Follow 2026-05-12 07:45:10 +00:00
a changed title from Incoming Follow activity gets undone by Undo of a different Follow to Incoming Follow activity gets undone by (re)handling Undo of a different Follow 2026-05-12 21:37:54 +00:00
Owner

I think this issue is due to out-of-order processing, combined with confusion about Undo.Follow.id for some reason. […] The deletions occur despite the Undo.Follow.id and Follow.id being different.

This seems plausible for the processed FR retraction ad readd, but probably doesn’t apply to the initial FR.

Indeed, in transmogrify.ex::handle_incoming_normalised/2, it simply checks for the object in the (required to be inlined (according to comments inlining is also hard-required by Mastodon)) undone Follow and later steps then look up the matching Follow activity based on the followee, follower pair.

Note though, Mastodon and I believe *key and likely many more will simply create the same (unresolvable) id for all follow activities with the same follower-followee pair. Checking the id will thus only help when the request comes from an implementation like *oma. But generally, retracting and resending a FR in rapid succession is simply not safe with common fedi implementations.

@norm looks like you originally added this in https://git.pleroma.social/pleroma/pleroma/pulls/3553 (merged via c2dcd767cf); do you still remember why it doesn’t use the Follow activities’ id? Were there some implementations federating FRs as transient activities? Or just a convenience decision since checking ids does not actually help for non-*oma remotes?
(Mastodon at least already used ids For Follow activities; albeit they were not and still aren’t actually unique for repeated requests)

> I think this issue is due to out-of-order processing, combined with confusion about Undo.Follow.id for some reason. […] The deletions occur despite the Undo.Follow.id and Follow.id being different. This seems plausible for the processed FR retraction ad readd, but probably doesn’t apply to the _initial_ FR. Indeed, in [`transmogrify.ex::handle_incoming_normalised/2`](https://akkoma.dev/AkkomaGang/akkoma/src/commit/22e3792ddbc0212fabf4820a02a1bf6724a6c27d/lib/pleroma/web/activity_pub/transmogrifier.ex#L649), it simply checks for the `object` in the *(required to be inlined (according to comments inlining is also hard-required by Mastodon))* undone `Follow` and later steps then look up the matching `Follow` activity based on the followee, follower pair. Note though, Mastodon and I believe \*key and likely many more will simply create the same (unresolvable) `id` for all follow activities with the same follower-followee pair. Checking the id will thus only help when the request comes from an implementation like \*oma. But generally, retracting and resending a FR in rapid succession is simply not safe with common fedi implementations. @norm looks like you originally added this in https://git.pleroma.social/pleroma/pleroma/pulls/3553 (merged via c2dcd767cf4abaa88da946e12ca643840b65e184); do you still remember why it doesn’t use the `Follow` activities’ `id`? Were there some implementations federating FRs as transient activities? Or just a convenience decision since checking ids does not actually help for non-\*oma remotes? *(Mastodon at least already used ids For Follow activities; albeit they were not and still aren’t actually unique for repeated requests)*
Author
Contributor

Mastodon and I believe *key and likely many more will simply create the same (unresolvable) id for all follow activities with the same follower-followee pair. Checking the id will thus only help when the request comes from an implementation like *oma. But generally, retracting and resending a FR in rapid succession is simply not safe with common fedi implementations.

Ah, right. >_<

Misskey used to do that, but later switched to generating ids for each Follow (https://github.com/misskey-dev/misskey/pull/10600), but they generate those ids local to the Misskey server instead of using the original id (https://github.com/misskey-dev/misskey/issues/11015).

Mastodon I think generates a different id for each Follow, but the id is a fragment id. mastodon/mastodon@bbb3392dbe/app/serializers/activitypub/follow_serializer.rb (L8)

generally, retracting and resending a FR in rapid succession is simply not safe with common fedi implementations

I don't think any fedi implementations warn users about this, do they? Nor do they apply any mitigations to ease this concern.

It's the kind of thing where it makes sense when you say it, but it's not obvious beforehand. Like the Egg of Columbus. I'm probably one of the people who ought to be most familiar with the inner workings of fedi, and even I didn't really consider this as something I was distinctly aware of -- at least enough that it didn't come to mind as a possible explanation for what was going on. (Granted, I was expecting the different ids to possibly be understood, so I wasn't expecting that it might not be understood.)

It's not clear what the correct behavior should be in all cases, either. I'd definitely have to think about it for some time before I could come up with a proper description.

> Mastodon and I believe *key and likely many more will simply create the same (unresolvable) id for all follow activities with the same follower-followee pair. Checking the id will thus only help when the request comes from an implementation like *oma. But generally, retracting and resending a FR in rapid succession is simply not safe with common fedi implementations. Ah, right. >_< Misskey used to do that, but later switched to generating ids for each Follow (<https://github.com/misskey-dev/misskey/pull/10600>), but they generate those ids local to the Misskey server instead of using the original id (<https://github.com/misskey-dev/misskey/issues/11015>). Mastodon I think generates a different id for each Follow, but the id is a fragment id. https://github.com/mastodon/mastodon/blob/bbb3392dbe35da834b0f31a5d3b15b19480fb688/app/serializers/activitypub/follow_serializer.rb#L8 > generally, retracting and resending a FR in rapid succession is simply not safe with common fedi implementations I don't think any fedi implementations warn users about this, do they? Nor do they apply any mitigations to ease this concern. It's the kind of thing where it makes sense when you say it, but it's not obvious beforehand. Like the Egg of Columbus. I'm probably one of the people who ought to be most familiar with the inner workings of fedi, and even I didn't really consider this as something I was distinctly aware of -- at least enough that it didn't come to mind as a possible explanation for what was going on. (Granted, I was expecting the different ids to possibly be understood, so I wasn't expecting that it might *not* be understood.) It's not clear what the correct behavior should be in all cases, either. I'd definitely have to think about it for some time before I could come up with a proper description.
Owner

Mastodon I think generates a different id for each Follow, but the id is a fragment id. mastodon/mastodon@bbb3392dbe/app/serializers/activitypub/follow_serializer.rb (L8)

What is object in this file? Since the closes thing to a definition in this file I saw is attribute :virtual_object, key: :object and virtual_object appears to be the target account, I was assuming object too refers to the target account, and thus only the follower-followee pair is used in the activitie’s id. But I’m not familiar with ruby and rails and might be misunderstanding what’s going on there

> Mastodon I think generates a different id for each Follow, but the id is a fragment id. https://github.com/mastodon/mastodon/blob/bbb3392dbe35da834b0f31a5d3b15b19480fb688/app/serializers/activitypub/follow_serializer.rb#L8 What is `object` in this file? Since the closes thing to a definition in this file I saw is `attribute :virtual_object, key: :object` and `virtual_object` appears to be the target account, I was assuming `object` too refers to the target account, and thus only the follower-followee pair is used in the activitie’s `id`. But I’m not familiar with ruby and rails and might be misunderstanding what’s going on there
Owner

Actually, I just remembered I now have a follow relationship to a Mastodon server and can thus check: the Follow activity id looks completely different then the second branch in the in the linked code but is just https://mastodon.example/<some-uuid>. I guess this means in current versions the first branch always(?) already works and while I can’t tell for sure, presumably the UUID is actually unique to each request

However, the test sample suggests Mastodon used to create https://mastodon.example/user/nameof/follows#id_of_sth URLs back when the ActivityPub Undo Follow code was added to *oma, so this might be a recent'ish development.

Actually, I just remembered I now have a follow relationship to a Mastodon server and can thus check: the `Follow` activity `id` looks completely different then the second branch in the in the linked code but is just `https://mastodon.example/<some-uuid>`. I guess this means in current versions the first branch always(?) already works and while I can’t tell for sure, presumably the UUID is actually unique to each request However, the test sample suggests Mastodon used to create `https://mastodon.example/user/nameof/follows#id_of_sth` URLs back when the ActivityPub `Undo` `Follow` code was added to \*oma, so this might be a recent'ish development.
Author
Contributor

I think the key point to focus on here is what to do when the Undo.activity.id isn't there, i.e. when it's undoing a Follow by description rather than by id.

If the id is there, then it should be used. If there is no existing activity with that id, then the Undo is deferred (because that Follow.id could arrive later). But later Follow activities where Follow.id != Undo.object.id shouldn't be dropped.1

If the id is not there, then we can use the description. If there is no existing activity with that description, then the Undo is either dropped (we don't expect to receive an anonymous Follow later?) or deferred (and then, does it Undo any matching Follow after this point? Or does it have a limited time window in which case it will do so? What should that time window be?)

Per mastodon/mastodon@8bbde181db/app/lib/activitypub/activity.rb (L98-L104) Mastodon will drop Create activities if a Delete for the same object arrived within the past 6 hours. It will also use that same "delete later" function (6 hours) for Undo activities, per https://github.com/mastodon/mastodon/blob/main/app/lib/activitypub/activity/undo.rb

I don't know if "wait 6 hours before refollowing someone" is sound advice or grounds for standardization, but if an Undo Follow without an Undo.object.id ends up undoing any Follow that matches the description, then it could be.

Note that in the normal case, an Undo Follow should never arrive before a Follow, but when it does, it can cause problems like this if there isn't an existing Follow to be undone.


  1. The "Misskey uses a local id for all Follows" issue shouldn't be a problem for Undo Follow, because in that case, the Undo and the Follow are on the same instance and the Follow.id should be correct and known by Akkoma. However, it would cause problems for Reject Follow if the Follow.id is checked and found to be invalid due to same-origin concerns or due to the Reject.object.id not being found in Akkoma's activities table. ↩︎

I think the key point to focus on here is what to do when the Undo.activity.id isn't there, i.e. when it's undoing a Follow by description rather than by id. If the id is there, then it should be used. If there is no existing activity with that id, then the Undo is deferred (because that Follow.id could arrive later). But later Follow activities where Follow.id != Undo.object.id shouldn't be dropped.[^1] If the id is not there, then we can use the description. If there is no existing activity with that description, then the Undo is either dropped (we don't expect to receive an anonymous Follow later?) or deferred (and then, does it Undo *any* matching Follow after this point? Or does it have a limited time window in which case it will do so? What should that time window be?) Per https://github.com/mastodon/mastodon/blob/8bbde181db0d6d79018fb1754a8296e753d47415/app/lib/activitypub/activity.rb#L98-L104 Mastodon will drop Create activities if a Delete for the same object arrived within the past 6 hours. It will also use that same "delete later" function (6 hours) for Undo activities, per https://github.com/mastodon/mastodon/blob/main/app/lib/activitypub/activity/undo.rb I don't know if "wait 6 hours before refollowing someone" is sound advice or grounds for standardization, but if an Undo Follow without an Undo.object.id ends up undoing *any* Follow that matches the description, then it could be. Note that in the normal case, an Undo Follow should never arrive before a Follow, but when it does, it can cause problems like this if there isn't an existing Follow to be undone. [^1]: The "Misskey uses a local id for all Follows" issue shouldn't be a problem for Undo Follow, because in that case, the Undo and the Follow are on the same instance and the Follow.id should be correct and known by Akkoma. However, it would cause problems for Reject Follow if the Follow.id is checked and found to be invalid due to same-origin concerns or due to the Reject.object.id not being found in Akkoma's activities table.
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#1120
No description provided.