Scalar tags parameter accepted into review queue causes HTTP 500 on approval

AI-generated summary

This bug report details an issue where a scalar string value for the tags parameter is accepted by POST /posts and stored in the review queue, causing an HTTP 500 error when a moderator attempts to approve the post. scavin explains that while clients should send an array of tags, the system incorrectly accepts a string (e.g., "macos"). This malformed data is passed directly into the ReviewableQueuedPost payload without normalization because create_options only converts values if they are already arrays. Consequently, when approval triggers DiscourseTagging.tags_for_saving, it attempts to perform set subtraction on a String object, raising a NoMethodError.

The reporter provided evidence from their self-hosted instance (version 2026.10.0-latest), showing the original request parameters, the stored JSON payload containing the scalar tag, and the resulting stack trace. The UI displayed the tag as individual characters (m, a, c, o, s) prior to the failure. A temporary workaround involves editing the reviewable to replace the scalar with a proper array of tag objects, though this does not address the root cause. The expected behavior is for the API to reject scalar tag inputs with a clear validation error or normalize them appropriately, rather than allowing invalid data to enter the queue and break the moderation workflow.

Approving a queued topic on our self-hosted site failed with HTTP 500 because the queued record contained payload.tags = "macos" (a string). The malformed value had already been accepted by POST /posts.

I understand that clients should send an array of tags. The issue I am reporting is that an invalid scalar can be accepted into the review queue and only fail later, when a moderator approves the post.

Version: 2026.10.0-latest, commit 3f2bf92368c78a7fa9a755f3d046adf35dd4459b.

Evidence from the incident

The post required approval because of approve_post_count.

At 2026-10-22 13:37:52 UTC, the original Rails request parameters already contained:

"tags" => "macos"

The request was POST /posts and the corresponding access log recorded HTTP 200. The submitted parameter names were title, raw, category, and tags.

Reading the pending reviewable confirmed:

{
  "type": "ReviewableQueuedPost",
  "status": 0,
  "version": 0,
  "topic_id": null,
  "payload": {
    "tags": "macos"
  }
}

This is a relevant-field excerpt, with the title, body, and user information omitted. The review UI displayed the tag as five separate letters: m, a, c, o, s.

Clicking Approve produced this exception (five grouped occurrences in the logs):

NoMethodError (undefined method '-' for an instance of String)
lib/discourse_tagging.rb:932:in 'DiscourseTagging.tags_for_saving'
lib/discourse_tagging.rb:43:in 'DiscourseTagging.tag_topic_by_names'
lib/discourse_tagging.rb:30:in 'DiscourseTagging.tag_topic'
lib/topic_creator.rb:228:in 'TopicCreator#setup_tags'
lib/topic_creator.rb:60:in 'TopicCreator#create'
lib/post_creator.rb:519:in 'PostCreator#create_topic'
lib/post_creator.rb:209:in 'block in PostCreator#create'
lib/post_creator.rb:419:in 'block in PostCreator#transaction'
lib/post_creator.rb:419:in 'PostCreator#transaction'
lib/post_creator.rb:207:in 'PostCreator#create'
app/models/reviewable_queued_post.rb:110:in 'ReviewableQueuedPost#perform_approve_post'
app/models/reviewable.rb:433:in 'Kernel#public_send'
app/models/reviewable.rb:433:in 'block in Reviewable#perform'
app/models/reviewable.rb:431:in 'Reviewable#perform'
app/controllers/reviewables_controller.rb:287:in 'ReviewablesController#perform'

Relevant code

These are the code paths checked in the running container at the commit above:

Expected behavior

Reject a scalar tags parameter with a clear validation error before creating the reviewable, or normalize it if that is the intended compatibility behavior. An existing malformed reviewable should also fail gracefully instead of causing an unhandled 500 during moderation.

Workaround and limits of this report

Editing the reviewable, removing the old tag, selecting the existing macos tag, and saving changed payload.tags into an array of tag objects. Readback confirmed version 1, still pending, and the UI showed one tag again. I have not retried approval after that change.

The original submitter’s client is unknown. This report does not establish that the native composer generates scalar tags. It is based on the actual request, stored reviewable, exception, and inspected code; I have not separately reproduced it on a clean installation.

For a development reproduction, the path to test is: require approval for a non-staff user’s post, submit POST /posts with a scalar tags: "macos" and otherwise valid fields, then approve the resulting queued post as an administrator.

There is an older API discussion with the same String subtraction exception. This report focuses on the invalid value being accepted into the review queue and breaking approval later.