{"record":{"id":"83acf5482b114aa0","repo":"instructure/canvas-lms","slug":"error-message","errorCode":null,"errorMessage":"#{error_message}","messagePattern":"#\\{error_message\\}","errorType":"exception","errorClass":"PlagiarismSubscriptionError","httpStatus":null,"severity":"error","filePath":"lib/lti/plagiarism_subscriptions_helper.rb","lineNumber":49,"sourceCode":"                     assignment_updated\n                     assignment_created].freeze\n\n    def initialize(tool_proxy)\n      @tool_proxy = tool_proxy\n      @product_family = tool_proxy.product_family\n    end\n\n    def create_subscription\n      Rails.logger.info { \"in: PlagiarismSubscriptionsHelper::create_subscription, tool_proxy_id: #{tool_proxy.id}\" }\n      if Services::LiveEventsSubscriptionService.disabled?\n        Rails.logger.info { \"Live Event Subscription Service disabled.  No subscription created.\" }\n        return\n      end\n\n      if Services::LiveEventsSubscriptionService.available?\n        subscription = plagiarism_subscription(tool_proxy, product_family)\n        result = Services::LiveEventsSubscriptionService.create_tool_proxy_subscription(tool_proxy, subscription)\n        raise PlagiarismSubscriptionError, error_message unless result.ok?\n\n        result.parsed_response[\"Id\"]\n      else\n        raise PlagiarismSubscriptionError, I18n.t(\"Live events subscriptions service is not configured\")\n      end\n    end\n\n    def plagiarism_subscription(tool_proxy, _product_family)\n      {\n        SystemEventTypes: EVENT_TYPES,\n        UserEventTypes: EVENT_TYPES,\n        ContextType: \"root_account\",\n        ContextId: tool_proxy.context.root_account.uuid,\n        Format: format,\n        TransportType: transport_type,\n        TransportMetadata: transport_metadata,\n        AssociatedIntegrationId: tool_proxy.guid\n      }.with_indifferent_access","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/lti/plagiarism_subscriptions_helper.rb#L31-L67","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nraise PlagiarismSubscriptionError, error_message unless result.ok?\n// after (caller-side)\nbegin\n  id = helper.create_subscription(tool_proxy, product_family)\nrescue Lti::PlagiarismSubscriptionsHelper::PlagiarismSubscriptionError => e\n  Rails.logger.warn(\"plagiarism subscription failed: #{e.message}\")\nend","handlingStrategy":"try-catch","validationCode":"abort unless Services::LiveEventsSubscriptionService.available?\nresult = Services::LiveEventsSubscriptionService.create_tool_proxy_subscription(tp, sub)\nabort 'service returned failure' unless result.ok?","typeGuard":"def subscription_ok?(result)\n  result.respond_to?(:ok?) && result.ok?\nend","tryCatchPattern":"begin\n  id = Lti::PlagiarismSubscriptionsHelper.new(course).create_subscription(tp, pf)\nrescue Lti::PlagiarismSubscriptionsHelper::PlagiarismSubscriptionError => e\n  Rails.logger.error(\"subscription create failed: #{e.message}\")\nend","preventionTips":["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."],"tags":["live-events","plagiarism","subscription","http"],"backgroundTag":"api-error-response","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}