discourse/discourse · error · RuntimeError

Contract violation: Query ends with a semicolon. Remove the

Error message

Contract violation:
Query ends with a semicolon. Remove the semicolon; your sql will be used in a subquery.

What it means

Error "Contract violation: Query ends with a semicolon. Remove the semicolon; your sql will be used in a subquery." thrown in discourse/discourse.

Source

Thrown at app/services/badge_granter.rb:316

        raise "Contract violation:\nQuery is triggered, but does not reference the ':backfill' parameter.\n(Hint: if :backfill is TRUE, you should ignore the :post_ids/:user_ids)"
      end
    end

    # TODO these three conditions have a lot of false negatives
    if opts[:target_posts]
      unless sql.match(/post_id/)
        raise "Contract violation:\nQuery targets posts, but does not return a 'post_id' column"
      end
    end

    unless sql.match(/user_id/)
      raise "Contract violation:\nQuery does not return a 'user_id' column"
    end
    unless sql.match(/granted_at/)
      raise "Contract violation:\nQuery does not return a 'granted_at' column"
    end
    if sql.match(/;\s*\z/)
      raise "Contract violation:\nQuery ends with a semicolon. Remove the semicolon; your sql will be used in a subquery."
    end
  end

  # Options:
  #   :target_posts - whether the badge targets posts
  #   :trigger - the Badge::Trigger id
  #   :explain - return the EXPLAIN query
  def self.preview(sql, opts = {})
    params = { user_ids: [], post_ids: [], backfill: true }

    BadgeGranter.contract_checks!(sql, opts)

    # hack to allow for params, otherwise sanitizer will trigger sprintf
    count_sql = <<~SQL
      SELECT COUNT(*) count
                 FROM (
                        #{sql}
                      ) q

View on GitHub (pinned to 1b2d7253e8)

When it happens

Trigger: Thrown at app/services/badge_granter.rb:316 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of discourse/discourse@1b2d7253e8 (2026-08-26). Data as JSON: /api/errors/66310edf55da8996. Report an issue: GitHub.