TypeError when submitting a flag with custom content (require_message flags)

Bug Description

When a user selects a flag type that requires a custom message (e.g. notify_moderators, notify_user, or any admin-created custom flag with “Require message” enabled), fills in the message, and submits — the browser throws an uncaught TypeError and the flag is never submitted.

Steps to Reproduce

  1. Visit any topic post
  2. Click the flag button to open the flag modal
  3. Select a flag type that requires a message (e.g. “Something Else” / notify_moderators, or any custom flag created in Admin → Flags with “Require message” enabled)
  4. Type a message in the text area (long enough to pass the minimum length)
  5. Click the submit button

Expected behavior

The flag is submitted successfully.

Actual behavior

The flag modal closes but the flag is not submitted. The browser console shows:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'act')
    at n.create (flag.js:24:8)
    at S.createFlag (flag.gjs:205:32)
    at S.takeAction (flag.gjs:196:12)
    at m.perform (reviewable-bundled-action.gjs:47:17)
    at ej._boundaryActionHandler (select-kit.js:685:34)
    ...

Root Cause Analysis

The crash originates in Flag#create (flag.js:24):

create(flagModal, opts) {
  const postAction = this.postActionFor(flagModal); // returns undefined
  // ...
  postAction.act(...) // ← TypeError: Cannot read properties of undefined
}

postActionFor in PostFlag looks up the selected flag by numeric id inside actions_summary:

// post-flag.js
postActionFor(flagModal) {
  return flagModal.args.model.flagModel.actions_summary.find(
    (item) => item.id === flagModal.selected.id
  );
}

The backend PostSerializer only includes a flag entry in actions_summary when at least one of can_act, count, or acted is truthy:

result << summary if summary[:can_act] || summary[:count] || summary[:acted]

For require_message: true flags (which are all notify_type: true), post_can_act? sets can_act: false when already_did_flagging is true — i.e. the user has previously submitted any notify-type flag on that post. In that case the entry is absent from actions_summary, postActionFor returns undefined, and the crash follows.

Meanwhile Post#flagsAvailable (which controls what the user can select in the UI) uses the actionByName map built at page-load time, so the flag can still appear as selectable even after actions_summary no longer contains it.

Secondary bug

There is also a broken comparison in flag.gjs:

const NOTIFY_MODERATORS_KEY = "notify_moderators"; // string

get notifyModeratorsFlag() {
  return this.flagsAvailable.find((f) => f.id === NOTIFY_MODERATORS_KEY);
  //                                      ^ number   ^ string — always false
}

f.id is a number but NOTIFY_MODERATORS_KEY is a string, so === always returns false and notifyModeratorsFlag is always undefined. This breaks the “Flag for Review” button logic in flagForReview().

Suggested Fix

PostFlag#postActionFor should use the actionByName map (keyed by name_key) instead of searching actions_summary by id, consistent with how TopicFlag#postActionFor already works:

// post-flag.js
postActionFor(flagModal) {
  return flagModal.args.model.flagModel.actionByName[
    flagModal.selected.name_key
  ];
}

And notifyModeratorsFlag should compare by name_key instead of id:

get notifyModeratorsFlag() {
  return this.flagsAvailable.find((f) => f.name_key === NOTIFY_MODERATORS_KEY);
}

Discourse version

2026.5.0-latest

Reproducible on

  • Latest main branch
1 Like