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}
) qView 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.