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

  1. Inspect result.parsed_response and service logs to see the upstream error returned by the LiveEvents subscription service.
  2. Verify LiveEvents configuration (app_key, app_secret, subscription URL) in config/dynamic_settings or the subscriptions service settings.
  3. Check whether a subscription already exists for the tool proxy and delete/update it before recreating.
  4. 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

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


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_access

View on GitHub (pinned to 1c9f0bb801)