instructure/canvas-lms · error · PlagiarismSubscriptionError
#
Error message
#{error_message} What it means
Lti::PlagiarismSubscriptionsHelper#create_subscription registers a plagiarism-detection LiveEvents subscription for a tool proxy via Services::LiveEventsSubscriptionService. When the service responds with a non-OK result (HTTP error or failure payload), the helper raises PlagiarismSubscriptionError whose message comes from #error_message. The subscription was not created and no subscription Id is returned.
Solutions
- Inspect result.parsed_response and service logs to see the upstream error returned by the LiveEvents subscription service.
- Verify LiveEvents configuration (app_key, app_secret, subscription URL) in config/dynamic_settings or the subscriptions service settings.
- Check whether a subscription already exists for the tool proxy and delete/update it before recreating.
- Retry after fixing the upstream issue; the error is a pass-through of the service's non-OK response.
Example fix
// before
raise PlagiarismSubscriptionError, error_message unless result.ok?
// after (caller-side)
begin
id = helper.create_subscription(tool_proxy, product_family)
rescue Lti::PlagiarismSubscriptionsHelper::PlagiarismSubscriptionError => e
Rails.logger.warn("plagiarism subscription failed: #{e.message}")
end Defensive patterns
Strategy: try-catch
Validate before calling
abort unless Services::LiveEventsSubscriptionService.available? result = Services::LiveEventsSubscriptionService.create_tool_proxy_subscription(tp, sub) abort 'service returned failure' unless result.ok?
Type guard
def subscription_ok?(result) result.respond_to?(:ok?) && result.ok? end
Try / catch
begin
id = Lti::PlagiarismSubscriptionsHelper.new(course).create_subscription(tp, pf)
rescue Lti::PlagiarismSubscriptionsHelper::PlagiarismSubscriptionError => e
Rails.logger.error("subscription create failed: #{e.message}")
end Prevention
- Keep LiveEvents service credentials valid and rotated on schedule.
- Log result.parsed_response on failures to expose upstream errors.
- Check for existing subscriptions before creating duplicates.
When it happens
Trigger: Calling create_subscription when the LiveEvents subscription service is available but create_tool_proxy_subscription returns result.ok? == false — e.g. the service rejects the subscription payload, returns 4xx/5xx, or the configured JWT/JWK credentials are invalid.
Common situations: LiveEvents service credentials (app key/secret) misconfigured or expired; the subscriptions endpoint URL is wrong or unreachable upstream; duplicate subscription; payload fails service-side validation for the tool proxy.
Related errors
- Live events subscriptions service is not configured
- Content Export for Outcomes Service failed
- Content-Type must be 'application/xml'
- could not retrieve configuration, the server response timed…
- Couldn't send LTI AGS grade progress metric
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/83acf5482b114aa0.
Report an issue: GitHub.
Appendix: source
Thrown at lib/lti/plagiarism_subscriptions_helper.rb:49
assignment_updated
assignment_created].freeze
def initialize(tool_proxy)
@tool_proxy = tool_proxy
@product_family = tool_proxy.product_family
end
def create_subscription
Rails.logger.info { "in: PlagiarismSubscriptionsHelper::create_subscription, tool_proxy_id: #{tool_proxy.id}" }
if Services::LiveEventsSubscriptionService.disabled?
Rails.logger.info { "Live Event Subscription Service disabled. No subscription created." }
return
end
if Services::LiveEventsSubscriptionService.available?
subscription = plagiarism_subscription(tool_proxy, product_family)
result = Services::LiveEventsSubscriptionService.create_tool_proxy_subscription(tool_proxy, subscription)
raise PlagiarismSubscriptionError, error_message unless result.ok?
result.parsed_response["Id"]
else
raise PlagiarismSubscriptionError, I18n.t("Live events subscriptions service is not configured")
end
end
def plagiarism_subscription(tool_proxy, _product_family)
{
SystemEventTypes: EVENT_TYPES,
UserEventTypes: EVENT_TYPES,
ContextType: "root_account",
ContextId: tool_proxy.context.root_account.uuid,
Format: format,
TransportType: transport_type,
TransportMetadata: transport_metadata,
AssociatedIntegrationId: tool_proxy.guid
}.with_indifferent_accessView on GitHub (pinned to 1c9f0bb801)