404 on earned badge event when badge is disabled

AI-generated summary

Users reported a 404 error when clicking on earned badge notifications after those badges were disabled on a Discourse instance (gvanrossum). The issue arises because notifications are stored with JSON data referencing badge IDs, and disabling badges doesn’t remove or update those notifications. sam acknowledged the problem, explaining that performance constraints prevent real-time validation of badge validity during notification rendering. He offered a workaround: manually deleting notifications via Rails console, and proposed future improvements — aligning client/server 404 pages and allowing custom 404 messages (e.g., “Badge is no longer enabled”). gvanrossum suggested an alternative: disable badge awarding and display in lists, but keep badge pages accessible with a note indicating they’re disabled. A related discussion was referenced by Moin.

A few of us created a new Discourse instance and started using it for a bit. Then we decided to disable most badges. By then I had already earned some of the disabled badges. The badge events stuck around in my personal “recents” list (the dropdown under my user icon) but when I clicked on them I got a 404 from our server. You may still be able to try this; this is an example:

IMO those events should be removed from my “recents” list (what do you call that thing?) or they should have told me something like “this badge has been disabled”.

(UPDATE: So maybe it’s not exactly a 404, but the response showed a “:(” icon with text “Page Not Found” and “Oops, the application tried to load a URL that doesn’t exist.” When you click on it from here you get a different but similar response: “Oops! That page doesn’t exist or is private.”)

5 „Gefällt mir“

Yes, I agree this is not ideal.

Our notifications table stores information about the “reason” in a text column containing json. This happens because there are a wide range of reasons you could be getting a notification and creating a big pile of columns would widen the table a lot and decrease flexibility around notifications.

We keep topic and post number, but there is a range of other types of ids (like group mentions, invites, badges and so on)

The “lookup my notifications” query needs to stay super fast, I did not want to force joins into 5 or so tables just to determine if the source is legit. (we do ensure post notifications are legit though - deleting a topic hides the related notifications)

Historically, people don’t go about disabling badges beyond the first few weeks of running a forum. So in practice this is a very minor issue.

That said there are 2 options you have here:

  • In practice nobody ever looks beyond the first 20 or so notifications, this issue will drown in a few days of active use.

  • If it is really bugging you you can clean up the old notifications from the table:

cd /var/discourse
./launcher enter app
rails c
% Notification.where('notification_type=12 AND data like \'%"badge_id":5,%\'').destroy_all

(5 is the badge id for welcome, destroy_all calls object model so it will live refresh all browsers)

On our side there are a couple of things I would like to clean up.

  1. Our “client side” 404 should look exactly like the server side 404, reloading a 404 page should always result in the same 404

  2. We should optionally be allowed to send in a bit more information into the 404 pages. As it stands we do a raise Discourse::NotFound we should allow raise Discourse::NotFound, "Badge is no longer enabled" and float that info into the 404.

7 „Gefällt mir“

Thanks! Or perhaps disabling a badge should be implemented differently, and
disabling should just stop awarding badges and displaying them in lists
that query for them, but still allow the badge page (like the one I
mentioned) to be hit. (Maybe adding a note “this badge has since been
disabled.”)

2 „Gefällt mir“